Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Экземпляры классов в сравнении с экземплярами классов

Поиск

 

Поскольку классы являются ссылочными типами, в переменной объекта класса хранится ссылка на адрес объекта в управляемой куче. Если первому объекту назначен второй объект того же типа, обе переменные ссылаются на объект, расположенный по данному адресу. Эта особенность обсуждается более подробно далее в этом разделе.

Экземпляры классов создаются с помощью оператора new. В следующем примере Person является типом, а person1 и person2 — являются экземплярами или объектами этого типа.

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

public class Person

{

public string Name { get; set; }

public int Age { get; set; }

public Person(string name, int age)

{

Name = name;

Age = age;

}

}

 

class Program

{

static void Main()

{

Person person1 = new Person("Anna", 24);

Console.WriteLine("person1 Имя = {0} Возраст = {1}", person1.Name, person1.Age);

Person person2 = person1;

person2.Name = "John";

person2.Age = 22;

Console.WriteLine("person2 Имя = {0} Возраст = {1}", person2.Name, person2.Age);

Console.WriteLine("person1 Имя = {0} Возраст = {1}", person1.Name, person1.Age);

Console.WriteLine("Для продолжение нажмите любую клавишу... ");

Console.ReadKey();

}

}

}

/* Выведет:

* person1 Имя = Anna Возраст = 24

* person2 Имя = John Возраст = 24

* person1 Имя = John Возраст = 22

* Для продолжение нажмите любую клавишу...

*/

 

Поскольку структуры являются типами значений, в переменной объекта структуры хранится копия всего объекта. Экземпляры структур также можно создать с помощью оператора new, однако он не является обязательным.

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

public struct Person

{

public string Name;

public int Age;

public Person(string name, int age)

{

Name = name;

Age = age;

}

}

 

public class Application

{

static void Main()

{

Person p1 = new Person("Alex", 9);

Console.WriteLine("p1 Имя = {0} Возраст = {1}", p1.Name, p1.Age);

Person p2 = p1;

p2.Name = "Spencer";

p2.Age = 7;

Console.WriteLine("p2 Имя = {0} Возраст = {1}", p2.Name, p2.Age);

Console.WriteLine("p1 Имя = {0} Возраст = {1}", p1.Name, p1.Age);

Console.WriteLine("Для продолжение нажмите любую клавишу...");

Console.ReadKey();

}

}

}

/* Выведет:

* p1 Имя = Alex Возраст = 9

* p2 Имя = Spencer Возраст = 7

* p1 Имя = Alex Возраст = 9

*/

 

Идентификация и равенство значений

 

Сравнивая два объекта на предмет равенства, сначала необходимо определить, нужно ли узнать, представляют ли две переменные один объект в памяти или значения одного или нескольких их полей являются равными. Если планируется сравнить значения, следует решить, являются ли объекты экземплярами типов значений (структурами) или ссылочными типами (классами, делегатами, массивами).

  • Чтобы определить, ссылаются ли два экземпляра класса на одно расположение в памяти (это значит, что они имеют одинаковый идентификатор), воспользуйтесь статическим методом Equals. (System.Object является неявным базовым классом для всех типов значений и ссылочных типов, включая структуры и классы, определенные пользователем).
  • Чтобы определить, имеют ли поля экземпляра в двух экземплярах структур одинаковые значения, воспользуйтесь методом ValueType.Equals. Поскольку все структуры неявно наследуют от System.ValueType, метод может быть вызван непосредственно в объекте, как показано в следующем примере.

 

Слегка перепишем код метода Main для классов предыдущего примера и добавим:

 

Person p1 = new Person("Bob The Zealot", 75);

Person p2 = new Person("1", 1);

p2 = p1;

// Выведет: p2 и p1 имеют одинаковые значения

if (p2.Equals(p1))

Console.WriteLine("p2 и p1 имеют одинаковые значения");

Console.WriteLine("Для продолжение нажмите любую клавишу... ");

Console.ReadKey();

 

Слегка перепишем код метода Main для структур предыдущего примера и добавим:

 

Person p1 = new Person("Wallace", 75);

Person p2;

p2.Name = "Wallace";

p2.Age = 75;

// Выведет: p2 и p1 имеют одинаковые значения

if (p2.Equals(p1))

Console.WriteLine("p2 и p1 имеют одинаковые значения");

Console.WriteLine("Для продолжение нажмите любую клавишу...");

Console.ReadKey();

 

Реализация System.ValueType Equals использует отображение, поскольку необходимо определить поля, находящиеся в любой структуре. При создании собственных структур переопределите метод Equals для предоставления эффективного алгоритма равенства, соответствующего вашему типу.

  • Чтобы определить, равны ли значения полей в двух экземплярах класса, можно воспользоваться методом Equals или оператором ==. Однако их следует использовать, только если они переопределены или перегружены классом, чтобы предоставить пользовательское определение равенства для объектов этого типа. Класс может также реализовывать интерфейс IEquatable<T>. или интерфейс IEqualityComparer<T>. Оба интерфейса предоставляют методы, которые можно использовать для проверки равенства значений. При создании собственных классов, переопределяющих Equals, следует выполнять инструкции из разделов Практическое руководство. Определение равенства значений для типа (Руководство по программированию на C#) (http://msdn.microsoft.com/ru-ru/library/dd183755.aspx) и Object.Equals(Object) (http://msdn.microsoft.com/ru-ru/library/bsc2ak47.aspx).

Понятие «Индексатор»

Понятие «Индексатор»

 

Понятие «Индексатор»

 

Индексаторы позволяют индексировать экземпляры класса или структуры так же, как массивы. Индексаторы напоминают свойства, но их методы доступа принимают параметры.

В следующем примере ниже, определяется универсальный класс и в качестве средств присвоения и извлечения значений создаются простые методы доступа get и set. Класс Program создает экземпляр этого класса для хранения строк и выводит всего одну строку:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

class SampleCollection<T>

{

// Объявляем массив для хранения данных элементов

private T[] arr = new T[100];

// Объявляем индексатор, который позволит клиенту код

// использующий [] при работе с классом

public T this[int i]

{

get

{

// Этот индексатор очень прост, и просто возвращает или задает соответствующий элемент из внутреннего массива

return arr[i];

}

set

{

arr[i] = value;

}

}

}

 

class Program

{

static void Main(string[] args)

{

// Объявите экземпляр SampleCollection.

SampleCollection<string> stringCollection = new SampleCollection<string>();

// Используем [] конструкцию на типе

stringCollection[0] = "Hello, world!";

Console.WriteLine(stringCollection[0]);

Console.WriteLine("Для продолжения нажмите любую клавишу... ");

Console.ReadKey();

}

}

}

 

Общие сведения об индексаторах:

  • Индексаторы позволяют индексировать объекты аналогично массивам.
  • Метод доступа get возвращает значение. Метод доступа set присваивает значение.
  • Ключевое слово this используется для определения индексаторов.
  • Ключевое слово value используется для определения значения, присваиваемого методом set индексатора.
  • Индексаторы не должны использовать в качестве индекса целочисленное значение; конкретный механизм поиска определяет разработчик.
  • Индексаторы можно перегружать.
  • Индексаторы могут иметь более одного формального параметра, например, при доступе к двухмерному массиву.

 

Использование индексаторов

 

Индексаторы являются синтаксическим удобством, позволяющим создавать класс, структуру или интерфейс, доступ к которому клиентские приложения получают, как к массиву. Чаще всего индексаторы реализуются в типах, главная цель которых — инкапсуляция внутренней коллекции или массива. Например, предположим, что имеется класс с именем «TempRecord», представляющий набор температур по шкале Фаренгейта, полученных в 10 различных моментов в течение 24 часов. Класс содержит массив с именем «temps» типа float, представляющий температуры, и DateTime, представляющий дату регистрации температур. Путём внедрения в этот класс индексатора, клиенты получат доступ к температурам в экземпляре TempRecord с помощью float temp = tr[4], а не float temp = tr.temps[4]. Использование индексатора не только упрощает синтаксис для клиентских приложений, но и делает класс и его назначение интуитивно понятными для других разработчиков.

Чтобы объявить, индексатор для класса или структуры, используйте ключевое слово this как показано в следующем примере:

 

public int this[int index] // Объявление индексатора

{

// get и set методы доступа

}

 

Сигнатура индексатора состоит из количества и типов его формальных параметров. В сигнатуру не включается тип индексатора или имена формальных параметров. Если в одном классе объявлено несколько индексаторов, у них должны быть различные сигнатуры.

Значение индексатора не классифицируется как переменная, поэтому не допускается передача значения индексатора как параметра ref или out.

Чтобы предоставить индексатору имя, которое можно использовать в других языках, используйте в объявлении атрибут name. Пример:

 

[System.Runtime.CompilerServices.IndexerName("TheItem")]

public int this[int index] // Объявление индексатора

{

// get и set методы доступа

}

 

Этот индексатор будет иметь имя TheItem. Если атрибут имени не предоставлен, используется имя по умолчанию Item.

 

В следующем примере показано, как объявить закрытое поле массива temps и индексатор. Индексатор обеспечивает прямой доступ к экземпляру tempRecord[i]. В качестве альтернативы применению индексатора можно объявить массив как член типа public осуществлять прямой доступ к его членам tempRecord.temps[i].

Обратим внимание, что при вычислении доступа индексатора, например, в инструкции Console.Write вызывается метод доступа get. Таким образом, если не существует метода доступа get, происходит ошибка времени компиляции:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

class TempRecord

{

// Массив температурных параметров

private float[] temps = new float[10] { 56.2F, 56.7F, 56.5F, 56.9F, 58.8F,

61.3F, 65.9F, 62.1F, 59.2F, 57.5F };

// Для клиентского код проверки входных данных при обращении к индексатору

public int Length

{

get { return temps.Length; }

}

// Объявление индексатора

// Если индекс находится вне диапазона, массив temps выдаст исключение

public float this[int index]

{

get

{

return temps[index];

}

 

set

{

temps[index] = value;

}

}

}

 

class Program

{

static void Main()

{

TempRecord tempRecord = new TempRecord();

// Используем метод доступа "set" индексатора

tempRecord[3] = 58.3F;

tempRecord[5] = 60.1F;

// Используем метод доступа "get" индексатора

for (int i = 0; i < 10; i++)

{

Console.WriteLine("Элемент #{0} = {1}", i, tempRecord[i]);

}

Console.WriteLine("Для продолжения нажмите любую клавишу... ");

Console.ReadKey();

}

}

}

/* Выведет:

* Элемент #0 = 56.2

* Элемент #1 = 56.7

* Элемент #2 = 56.5

* Элемент #3 = 58.3

* Элемент #4 = 58.8

* Элемент #5 = 60.1

* Элемент #6 = 65.9

* Элемент #7 = 62.1

* Элемент #8 = 59.2

* Элемент #9 = 57.5

* Для продолжения нажмите любую клавишу...

*/

 

C# не ограничивает тип индексатора типом «integer».Например, может оказаться полезным использовании в индексаторе строки (string). Такой индексатор можно реализовать, выполнив поиск строки в коллекции и возвратив соответствующее значением. Методы доступа можно перегружать, версии типа «string» и «integer» могут сосуществовать совместно в одном пространстве имён.

 

В этом примере объявляется класс, в котором хранятся дни недели. Объявляется метод доступа get, который принимает строку (название дня недели) и возвращает соответствующее целое число. Например, воскресенье возвращает 0, понедельник возвращает 1 и т. д. Неверный день недели вернёт -1 и выдаст исключение (прервёт выполнение):

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

class DayCollection

{

string[] days = { "Пн.", "Вт.", "Ср.", "Чт.", "Пт.", "Суб.", "Вск." };

 

// Находим день или вернём -1

private int GetDay(string testDay)

{

for (int j = 0; j < days.Length; j++)

{

if (days[j] == testDay)

{

return j;

}

}

throw new System.ArgumentOutOfRangeException(testDay, "testDay должен быть от \"Пн.\" до \"Вск.\"");

}

// "get" возвращает int для полученной строки

public int this[string day]

{

get

{

return (GetDay(day));

}

}

}

 

class Program

{

static void Main(string[] args)

{

DayCollection week = new DayCollection();

Console.WriteLine(week["Чт."]);

// Выводим ArgumentOutOfRangeException если указали неверную строку

Console.WriteLine(week["Указан 8-ой день недели"]);

Console.WriteLine("Для продолжения нажмите любую клавишу... ");

Console.ReadKey();

}

}

}

/* Выведет (после вывода выдаст исключение):

* 5

* Для продолжения нажмите любую клавишу...

*/

 

Существуют два основных способа повышения надежности и безопасности индексаторов.

  • Обрабатывать ошибки на тот случай, если код клиента передаст недопустимое значение индекса. В первом примере, приведённом ранее в этом разделе, класс TempRecord предоставляет свойство Length, позволяющее коду клиента проверить введенные данные перед тем, как передать их индексатору. Также можно поместить код обработки ошибки в индексатор.
  • Установить максимальное обоснованное ограничение доступности для методов доступа get и set. Это особенно важно для метода доступа set.

 

Индексаторы в интерфейсах

 

Индексаторы можно объявлять в интерфейсе (interface). Между методами доступа индексаторов интерфейса и методами доступа индексаторов класса существуют следующие отличия:

  • Методы доступа интерфейсов не используют модификаторы доступа.
  • Метод доступа интерфейса не имеет тела.

 

Поэтому метод доступа предназначен для того, чтобы указывать, доступен ли индексатор для чтения и записи, только для чтения или только для записи.

 

Ниже приведён пример метода доступа индексатора интерфейса:

 

public interface ISomeInterface

{

//...

 

// Индексатор

string this[int index]

{

get;

set;

}

}

 

Сигнатура индексатора должна отличаться от сигнатур всех других индексаторов, объявленных в том же интерфейсе.

 

В следующем примере показана реализация индексаторов интерфейса:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

// Интерфейс

public interface ISomeInterface

{

// Индексатор интерфейса

int this[int index]

{

get;

set;

}

}

 

// Реализация интерфейса

class IndexerClass: ISomeInterface

{

private int[] arr = new int[100];

public int this[int index] // Объявляем индексатор

{

get

{

// Объект arr выдаст исключение IndexOutOfRange

return arr[index];

}

set

{

arr[index] = value;

}

}

}

 

class Program

{

static void Main()

{

IndexerClass test = new IndexerClass();

Random rand = new Random();

// Вызываем индексатор для инициализации элементов класса test

for (int i = 0; i < 10; i++)

{

test[i] = rand.Next();

}

for (int i = 0; i < 10; i++)

{

Console.WriteLine("Элемент #{0} = {1}", i, test[i]);

}

 

Console.WriteLine("Для продолжения нажмите любую клавишу... ");

Console.ReadKey();

}

}

}

/* Выведет:

* Элемент #[От 0 до 9] = [Случайное число]

* Для продолжения нажмите любую клавишу...

*/

 

В предыдущем примере можно использовать явную реализацию члена интерфейса с помощью полного имени члена интерфейса. Однако, полное имя требуется для исключения неоднозначности только тогда, когда класс реализует более одного интерфейса с одинаковой сигнатурой индексатора. Например, если класс Employee реализует два интерфейса ICitizen и IEmployee с одинаковой сигнатурой индексатора, требуется явная реализация члена интерфейса.

 



Поделиться:


Последнее изменение этой страницы: 2016-08-15; просмотров: 434; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.145.106.176 (0.007 с.)