Какие аспекты поведения определяет тип system object
Все классы в .NET, даже те, которые мы сами создаем, а также базовые типы, такие как System.Int32 , являются неявно производными от класса Object. Даже если мы не указываем класс Object в качестве базового, по умолчанию неявно класс Object все равно стоит на вершине иерархии наследования. Поэтому все типы и классы могут реализовать те методы, которые определены в классе System.Object. Рассмотрим эти методы.
ToString
Метод ToString служит для получения строкового представления данного объекта. Для базовых типов просто будет выводиться их строковое значение:
int i = 5; Console.WriteLine(i.ToString()); // выведет число 5 double d = 3.5; Console.WriteLine(d.ToString()); // выведет число 3,5
Для классов же этот метод выводит полное название класса с указанием пространства имен, в котором определен этот класс. И мы можем переопределить данный метод. Посмотрим на примере:
Person person = new Person < Name = "Tom" >; Console.WriteLine(person.ToString()); // выведет название класса Person Clock clock = new Clock < Hours = 15, Minutes = 34, Seconds = 53 >; Console.WriteLine(clock.ToString()); // выведет 15:34:53 class Clock < public int Hours < get; set; >public int Minutes < get; set; >public int Seconds < get; set; >public override string ToString() < return $"::"; > > class Person < public string Name < get; set; >= ""; >
Для переопределения метода ToString() в классе Clock, который представляет часы, используется ключевое слово override (как и при обычном переопределении виртуальных или абстрактных методов). В данном случае метод ToString() выводит в строке значения свойств Hours, Minutes, Seconds.
Класс Person не переопределяет метод ToString, поэтому для этого класса срабатывает стандартная реализация этого метода, которая выводит просто название класса.
Кстати в данном случае мы могли задействовать обе реализации:
Person tom = new Person < Name = "Tom" >; Console.WriteLine(tom.ToString()); // Tom Person undefined = new Person(); Console.WriteLine(undefined.ToString()); // Person class Person < public string Name < get; set; >= ""; public override string? ToString() < if (string.IsNullOrEmpty(Name)) return base.ToString(); return Name; >>
То есть если имя — свойство Name не имеет значения, оно представляет пустую строку, то возвращается базовая реализация — название класса. Стоит отметить, что базовая реализация возвращает не просто строку, а объект string? — то есть это может быть строка string, либо значение null , которое указывает на отсутствие значения. И в реальности в качестве возвращаемого типа для метода мы можем использовать как string , так и string?
Если же имя у объекта Person установлено, то возвращается значение свойства Name. Для проверки строки на наличие значения применяется метод String.IsNullOrEmpty() .
Стоит отметить, что различные технологии на платформе .NET активно используют метод ToString для разных целей. В частности, тот же метод Console.WriteLine() по умолчанию выводит именно строковое представление объекта. Поэтому, если нам надо вывести строковое представление объекта на консоль, то при передаче объекта в метод Console.WriteLine необязательно использовать метод ToString() — он вызывается неявно:
Person person = new Person < Name = "Tom" >; Console.WriteLine(person); // Tom Clock clock = new Clock < Hours = 15, Minutes = 34, Seconds = 53 >; Console.WriteLine(clock); // выведет 15:34:53
Метод GetHashCode
Метод GetHashCode позволяет возвратить некоторое числовое значение, которое будет соответствовать данному объекту или его хэш-код. По данному числу, например, можно сравнивать объекты. Можно определять самые разные алгоритмы генерации подобного числа или взять реализацию базового типа:
class Person < public string Name < get; set; >= ""; public override int GetHashCode() < return Name.GetHashCode(); >>
В данном случае метод GetHashCode возвращает хеш-код для значения свойства Name. То есть два объекта Person, которые имеют одно и то же имя, будут возвращать один и тот же хеш-код. Однако в реальности алгоритм может быть самым различным.
Получение типа объекта и метод GetType
Метод GetType позволяет получить тип данного объекта:
Person person = new Person < Name = "Tom" >; Console.WriteLine(person.GetType()); // Person
Этот метод возвращает объект Type , то есть тип объекта.
С помощью ключевого слова typeof мы получаем тип класса и сравниваем его с типом объекта. И если этот объект представляет тип Person, то выполняем определенные действия.
object person = new Person < Name = "Tom" >; if (person.GetType() == typeof(Person)) Console.WriteLine("Это реально класс Person");
Причем поскольку класс Object является базовым типом для всех классов, то мы можем переменной типа object присвоить объект любого типа. Однако для этой переменной метод GetType все равно вернет тот тип, на объект которого ссылается переменная. То есть в данном случае объект типа Person.
Стоит отметить, что проверку типа в примере выше можно сократить с помощью оператора is :
object person = new Person < Name = "Tom" >; if (person is Person) Console.WriteLine("Это реально класс Person");
В отличие от методов ToString, Equals, GetHashCode метод GetType() не переопределяется.
Метод Equals
Метод Equals позволяет сравнить два объекта на равенство. В качестве параметра он принимает объект для сравнения в виде типа object и возврашает true , если оба объекта равны:
public override bool Equals(object? obj)
Например, реализуем данный метод в классе Person:
class Person < public string Name < get; set; >= ""; public override bool Equals(object? obj) < // если параметр метода представляет тип Person // то возвращаем true, если имена совпадают if (obj is Person person) return Name == person.Name; return false; >// вместе с методом Equals следует реализовать метод GetHashCode public override int GetHashCode() => Name.GetHashCode(); >
Метод Equals принимает в качестве параметра объект любого типа, который мы затем приводим к текущему классу — классу Person.
Если переданный объект представляет тип Person, то возвращаем результат сравнения имен двух объектов Person. Если же объект представляет другой тип, то возвращается false.
В данном случае для примера применяется довольно простой алгоритм сравнения, однако при необходимости реализацию метода можно сделать более сложной, например, сравнивать по нескольким свойствам при их наличии.
Стоит отметить, что вместе с методом Equals следует реализовать метод GetHashCode.
var person1 = new Person < Name = "Tom" >; var person2 = new Person < Name = "Bob" >; var person3 = new Person < Name = "Tom" >; bool person1EqualsPerson2 = person1.Equals(person2); // false bool person1EqualsPerson3 = person1.Equals(person3); // true Console.WriteLine(person1EqualsPerson2); // false Console.WriteLine(person1EqualsPerson3); // true
И если следует сравнивать два сложных объекта, как в данном случае, то лучше использовать метод Equals, а не стандартную операцию ==.
8.4. Класс System.Object
В языке С# все типы данных (как структурные, так и ссылочные) производятся от единого общего предка: класса System.Object. Класс System.Object определяет общее полиморфическое поведение для всех типов данных во вселенной .NET. Во всех предыдущих примерах у нас не было необходимости явно указывать класс System.Object в качестве базового — это подразумевалось. Однако нам ничто не мешает сделать это, явно указав, что наш класс производится от System.Object: class Figure : System.Object ………. …… > Как и в любом другом классе С#, в классе System.Object существует свой набор членов. Некоторые члены определены как виртуальные и должны быть замещены в определении производного класса (листинг 8.8) Листинг 8.8. Фрагмент определения базового класса System.Object в пространстве имен System namespace System public class Object public Object(); public virtual Boolean Equals (Object obj); public virtual Int32 GetHashCode(); public Type GetType(); public virtual String ToString(); protected virtual void Finalize(); projected Object MemberwiseClone(); > ……………… > Предназначение наиболее часто используемых методов базового класса System.Object описано в табл. 8.1, а пример их применения приведен в программе, код которой представлен в листинге 8.9. Таблица 8.1.
Главные методы объекта System.Object
Метод | Назначение |
Equals() | По умолчанию этот метод возвращает «истинно» только тогда, когда сравниваемые сущности указывают на одну и ту же область в оперативной памяти. Поэтому этот метод в его исходной реализации предназначен только для сравнения объектов ссылочных типов, но не структурных. Для нормальной работы с объектами структурных типов этот метод необходимо заместить. Однако помните, что если вы замещаете этот метод, вам потребуется также заместить метод GetHashCode() |
GetHashCode() | Возвращает целочисленное значение, идентифицирующее конкретный экземпляр объекта данного типа. |
GetType() | Метод возвращает объект Туре(), полностью описывающий тот объект, из которого метод был вызван. |
ToString() | Возвращает символьное представление объекта в формате . (такой формат носит также название «полностью определенного имени» — fully qualified name). Если тип определен вне какого-либо пространства имен, возвращается только имя класса. Этот метод может быть замещен для представления информации о внутреннем состоянии объекта (в формате имя — значение). |
Finalize() | Пока мы будем считать, что основное назначение этого метода — освободить все ресурсы, занятые объектом данного класса, перед удалением этого объекта. |
MemberwiseClone(); | Этот метод предназначен для создания еще одной ссылки на область, занимаемую объектом данного типа в оперативной памяти. Этот метод не может быть замещен. Если вам потребовалось реализовать поддержку создания полной копии объекта в оперативной памяти, вы должны реализовать а вашем классе поддержку интерфейса ICloneable. |
Листинг 8.9. Демонстрация методов, унаследованных от класса System.Object using System; using System.Collections.Generic; using System.Text; namespace classSystemObject class ObjTest static void Main(string[] args) ObjTest c1 = new ObjTest(); // Выводим информацию на консоль Console.WriteLine(«ToString: «, c1.ToString()); Console.WriteLine(«Hash Code: «, c1.GetHashCode()); Console.WriteLine(«Type: «, c1.GetType().ToString()); // Создаем еще одну ссылку на c1 ObjTest c2 =c1; object o = c2; // Действительно ли все три экземпляра указывают на одну // и ту же область в оперативной памяти? if (o.Equals(c1) && c2.Equals(o)) Console.WriteLine(» Ссылки установлены на одну область памяти!»); > > > o continue Обратите внимание, что реализация по умолчанию метода ToString возвращает только имя того типа, из которого данный метод был вызван (ObjTest). Очень часто этот метод в производных классах замещается таким образом, чтобы возвращать информацию о внутреннем состоянии объекта. Замещение методов класса System.Object. Методы, которые наследуют типы данных от класса System.Object, во многих ситуациях исключительно полезны. Но обычно при создании своих собственных типов данных некоторые методы класса System.Object приходится замещать. Рассмотрим такое замещение на примере. Для целей нашего примера мы будем использовать класс Person (Листинг 8.10), в который мы поместим переменные для хранения данных об имени человека, его номере социального страхования и возрасте: Листинг 8.10. Замещение методов ToString, Equals и GetHashCode класса System.Object в классе Person using System; using System.Collections.Generic; using System.Text; namespace Person class Person // Данные о человеке public string FirstName; //Фамилия public string LastName; //Имя public string SSN; // номер социального страхования public byte age; //возраст // конструктор с параметрами public Person(string fname, string lname, string ssn, byte a) FirstName = fname; LastName = lname; SSN = ssn; age = a; > // замещаем метод Equals(), унаследованный от System.Object метод // возвращает истинно тогда, когда у сравниваемых объектов типа Person // внутренние состояния одинаковы (то есть значения полей // firstName, lastName, SSN и age совпадают) public override bool Equals(object o) // выполняется сравнение значений переменных объекта, принимаемого в // качестве параметра, со значениями переменных объекта, для которого // метод Equals был вызван (c помощью ключевого слова this) Person temp = (Person) o; if (temp.FirstName == this.FirstName && temp.LastName ==this.LastName && temp.SSN == this.SSN && temp.age == this.age) else return false; > // замещаем метод ToString(), унаследованный от System.Object так, чтобы // он возвращал не имя типа, а информацию о внутреннем состоянии объекта // возвращаемое значение типа string заключено в прямоугольные скобки [] public override string ToString() StringBuilder sb = new StringBuilder(); sb.AppendFormat(«[FirstName= «, this.FirstName); sb.AppendFormat(» LastName= «, this.LastName); sb.AppendFormat(» SSN= «, this.SSN); sb.AppendFormat(» Age= ]», this.age); return sb.ToString(); > // замещение метода GetHashCode() public override int GetHashCode() // Возвращаем хэш-код, основанный на номере социального страхования // (переменной SSN) return SSN.GetHashCode(); > > class Program static void Main(string[] args) Console.WriteLine(«***** Тестирование класса Person*****»); Person p1 = new Person(«Иванов«, «Иван«, «222-22-2222», 25); Person p2 = new Person(«Иванов«, «Иван«, «222-22-2222», 25); // используем замещенный метод Equals()для сравнения внутреннего // состояния (полей) объектов p1 и p2 if (p1.Equals(p2) && p1.GetHashCode() == p2.GetHashCode()) Console.WriteLine(«Объекты P1 и P2 одинаковы\n«); else Console.WriteLine(«Объекты P1 и P2 различны\n»); // меняем внутренне состояние объекта p2 p2.age = 2; // еще раз производим сравнение внутреннего состояния объектов p1 и p2 if (p1.Equals(p2) && p1.GetHashCode() == p2.GetHashCode()) Console.WriteLine(«Объекты P1 и P2 равны\n»); else Console.WriteLine(«Объекты P1 и P2 различны\n»); // Get stringified version of objects. Console.WriteLine(p1.ToString()); Console.WriteLine(p2); > > > Программа выводит следующие строки: ***** Тестирование класса Person***** Объекты P1 и P2 одинаковы Объекты P1 и P2 различны [FirstName= Иванов LastName= Иван SSN= 222-22-2222 Age= 25] [FirstName= Иванов LastName= Иван SSN= 222-22-2222 Age= 2] Примечание. При замещении метода EqualsO следует также заместить метод GetHashCode (). В случае, если это сделано не будет, компилятор выдаст предупреждение. Метод GetHashCode() возвращает числовое значение, идентифицирующее объект в оперативной памяти. Чаще всего это значение используется в коллекциях, работающих с хэш-кодами объектов.
14. Класс System.Object и иерархия типов
Диаграмма, показанная на рис. 3, связывает типы платформы .NET с точки зрения отношения наследования.
SByte | ||||
Массив A [] | Byte | |||
Int16 | ||||
Array | Класс C | Структура | S | UInt16 |
Int32 | ||||
String | Object | ValueType | UInt32 | |
Int64 | ||||
Delegate | Enum | UInt64 | ||
Single | ||||
MulticastDelegate | Перечисление E | Char | ||
Double | ||||
Делегат D | Boolean | |||
Decimal |
Рис. 3. Иерархия типов платформы .NET Все типы в .NET Framework наследуются (прямо или косвенно) от класса System. Object 1 (в C# для этого типа используется псевдоним object ). Тип System. ValueType является предком всех типов значений (включая числовые типы, пользовательские структуры и перечисления). Массивы наследуются от класса System. Array , а класс System. Delegate является предком всех делегатов. Рассмотрим элементы класса System. Object в алфавитном порядке. public virtual bool Equals( object obj) Данный метод определяет, равен ли объект obj текущему объекту. Реализация Equals() по умолчанию обеспечивает равенство ссылок для ссылочных типов и побитовое равенство для типов значений. Пользовательский тип можетпереопределять метод Equals() . При этом должны выполняться такие правила: 1. x.Equals(x) == true . 2. x.Equals(y) == y.Equals(x) . 3. (x.Equals(y) & y.Equals(z)) == true x.Equals(z) == true . 4. Вызовы метода x.Equals(y) возвращают одинаковое значение до техпор, пока объекты x и y остаются неизменными. 5. x.Equals( null ) == false , если x != null . 6. Метод Equals() не должен генерировать исключений. 1 Формально, от object не наследуются типы-указатели, используемые в неуправляемом коде (например, int * ), а также интерфейсы (но интерфейсы приводятся к object ).
Типы, переопределяющие метод Equals() , должны также переопределять метод GetHashCode() (и наоборот); в противном случае коллекции-словари могут работать неправильно. Если применяется перегрузка операции равенства для заданного типа, то этот тип также должен переопределять и метод Equals() . Реализация Equals() должна возвращать те же результаты, что и перегруженная операция равенства. public static bool Equals( object a, object b) Метод определяет, равны ли экземпляры a и b . Если оба аргумента равны null , метод возвращает true . Если только один аргумент равен null , возвращается false . Если оба аргумента не равны null , возвращается a.Equals(b) . protected virtual void Finalize() Метод Finalize() позволяет объекту попытаться освободить ресурсы и выполнить другие операции очистки, перед тем как объект будет утилизиро ван в процессе сборки мусора. public virtual int GetHashCode() Метод GetHashCode() играет роль хеш-функции для определённого типа. Этот метод можно использовать в алгоритмах хеширования и таких структурах данных, как хеш-таблицы. Реализация метода GetHashCode() по умолчанию не гарантирует уникальность возвращаемых кодов. Пользовательские типы могут переопределять данный метод для эффективного вычисления хеш-функции. Если два объекта при сравнении оказались равны, методы GetHashCode() этих объектов должны возвращать одинаковые значения. Однако если при сравнении оказалось, что объекты не равны, методы GetHashCode() не обязательно должны возвращать разные значения. public Type GetType() Данный метод возвращает объект System. Type для текущего экземпляра. Объект System. Type содержит метаданные, связанные с классом текущего экземпляра. protected object MemberwiseClone() Метод MemberwiseClone() применяется для создания неполной копии объекта. Метод создаёт новый объект (конструктор при этом не вызывается), азатем копирует в него нестатические поля текущего объекта. Если поле относится к типу значения, выполняется побитовое копирование полей. Если поле относится к ссылочному типу, копируются ссылки, а не объекты, на которые они указывают. Следовательно, ссылки в исходном объекте и его клоне указывают наодин и тот же объект.
public static bool ReferenceEquals( object a, object b) Этот статический метод возвращает значение true , если параметр a соответствует тому же экземпляру, что и параметр b , или же оба они равны null ; в противном случае метод возвращает false . public virtual string ToString() Метод ToString() возвращает строку, которой представлен текущий объект. Метод может быть переопределён в производном классе для возврата адекватных значений для данного типа. Так как System. Object является предком любого типа, переменной типа object можно присвоить любую переменную. Если для ссылочных типов при этом происходит только присваивание указателей, для типов значений выполняется специальная операция, называемая операцией упаковки (boxing) 1 . При упаковке в динамической памяти создаётся объект, содержащий значение переменной и информацию о её типе. Упакованный объект можно подвергнуть обратномупре- образованию – операции распаковки (unboxing).
object o = 123; | // | операция | упаковки |
int j = ( int )o; | // | операция | распаковки |
По форме операция распаковка выглядит как приведение типов, однако таковой не является. Следующий код при выполнении генерирует исключение:
object o = 123; | // | операция упаковки литерала int |
short j = ( short )o; | // | генерируется InvalidCastException |
При распаковке необходимо указывать точный тип упакованного объекта:
short j = ( short )( int )o; | // распаковка, затем приведение типов |
15. Структуры
Структура – это пользовательский тип значения, поддерживающий всю функциональность класса, кроме наследования. Пользовательская структура в простейшем случае позволяет инкапсулировать несколько полей различных типов. Но элементами структуры могут быть не только поля, а и методы, свойства, события, константы. Структуры также могут реализовывать интерфейсы. Синтаксис определения структуры следующий: модификаторы struct имя-структуры < элементы-структуры >1 Операция упаковки выполняется и в случае, когда переменной типа интерфейс присваивается переменная типа значения. Этот аспект будет разобран при рассмотрении интерфейсов.
При описании экземплярных полей структуры следует учитывать, что они не могут быть инициализированы при объявлении (для статических полей инициализация при объявлении возможна). Как и класс, структура может содержать конструкторы. В структуре можно объявить статический конструктор или экземплярный конструктор с параметрами, причём в теле конструктора необходимо инициализировать все поля структуры. Ещё одно отличие структуры от класса– в структуре указатель на экземпляр this доступен не только для чтения, но и для записи. Рассмотрим пример структуры для представления точки в пространстве: public struct Point3D < public readonly double X, Y, Z; public Point3D( double x, double y, double z = 0.0) < X = x; Y = y; Z = z; >public Point3D( Point3D point) < this = point; >> Если в типе объявляется поле-структура, все элементы структуры получат значения по умолчанию. Аналогичная ситуация будет при объявлении локальной переменной-структуры и вызове конструктора структуры без параметров 1 . Без вызова конструктора поля переменной-структуры не инициализированы. // поля p1 не инициализированы, их надо установить до использования Point3D p1; // поля p2 инициализированы значениями 0.0 Point3D p2 = new Point3D (); // поля p3 инициализированы значениями 2.0, 3.0, 0.0 Point3D p3 = new Point3D (2.0, 3.0); Локальные переменные структурного типа размещаются в стеке приложения. Структурные переменные можно присваивать друг другу, при этом выполняется копирование данных структуры на уровне полей. Все структуры наследуются от класса System. ValueType . Класс ValueType переопределяет некоторые 1 В отличие от классов, в структуре конструктор без параметров присутствует даже при объявлении пользовательского конструктора.
Учим C#: №4
abstract class Stone // это будет базовый класс
protected float weight;// protected можно смело
protected int color;// использовать в абстрактных классах
// т.к. они в любом случае наследуемы.
public Stone()// конструктор
public Stone(float w,int c) // конструктор
weigth=w;
color=c;
>
public abstract void Print(); // абстрактный метод вывода
>
class Diamond:Stone // это производный класс
private double price;
public Diamond()
public Diamond(float ww,int cc,double pp):base(ww,cc)
public override void Print() // метод вывода.
//o verride говорит компилятору о том,
// что код наследованного метода
// будет переопределен.
sc.WriteLine(«The weight is «+weight);
sc.WriteLine(«The color number is «+color);
// ключевое слово base позволяет получить
// доступ и к закрытым членам базового класса
// имеет смысл ссылки на базовый класс
sc.WriteLine(«The price is «+price);
sc.ReadLine();
>
>
class Prog
public static void Main()
Diamond ob1=new Diamond(10,4,99.99);
ob1.Print();
>
>
В .Net Framework имеется специальный класс — Object. А еще точнее
— System.Object. Этот класс является базовым для всех стандартных классов. Любой тип в C# — это, по сути, класс. Поэтому мы до сих пор не рассмотрели типы, а вот мучаемся тут с этими концепциями и реализациями. Зато потом будет понятнее архитектура. Я, например, в свое время пожалел, что сначала знакомился с типами. Да, но что-то я не о том. Object
это фундаментальный класс. Он определяет минимальный набор методов для объекта. Практически, все классы .Net Framework наследуют этот класс, и этот момент очень важен. Итак, класс System.Object определяет шесть открытых методов:
public virtual bool Equals(object object)
Этот метод определяет равенство вызывающего и передаваемого экземпляров.
public static bool Equals(object ob1,object ob2)
Этот метод определяет равенство объектов ob1 и ob2, передаваемых в качестве параметров. Метод Equals() перегружен, т.к. для него определено два разных контекста.
public virtual int GetHashCode()
Этот метод возвращает хэш-код, соответствующий вызывающему объекту. Применяется в алгоритмах, использующих хэши для доступа к экземплярам.
public Type GetType()
Этот метод возвращает тип объекта.
public static bool ReferenceEquals(object ob1,object ob2)
Этот метод определяет равенство ссылок ob1 и ob2, т.е. ссылаются ли они на один экземпляр.
public virtual string ToString()
Этот метод возвращает строку с описанием объекта.
protected void Finalyze()
Вызывается перед сборкой мусора. В принципе, можно не использовать. Не следует помещать в выполняемый код. Обычно вызывается деструктором.
protected object MemberwiseClone()
Один старый опытный кодер сказал мне, что этот метод используется для копирования объектов, при котором не копируется объект, а копируются его члены. Если честно, то я ему не верю, т.к. метод возвращает значение типа object. Этот метод скорее создает копию объекта, содержащую ссылки на другие объекты. С этой штукой я пока не
экспериментировал и за базар не отвечаю. Как-нибудь, я разберусь с этим, напишу.
Теперь можно переходить к интерфейсам с чистой душой. Интерфейсами в C# принято называть структуры, содержащие прототипы методов. Интерфейс, как и абстрактный класс не может являться базой для создания объектов. Синтаксис интерфейса похож на синтаксис абстрактного класса, но интерфейс не может содержать переменные-члены и тела методов, строго прототипы. А во всем остальном, можно считать их одной и той же сущностью. Все методы интерфейса являются виртуальными. Для создания интерфейсов в C# используется ключевое слово interface. Ниже приведен общий вид интерфейса и его наследования:
public interface имя_интерфейса // определение интерфейса
тип_1 метод_1(список_параметров_1); // прототип_1
.
тип_n метод_n(список_параметров_n); // прототип_n
>
class имя_класса:имя_интерфейса
// наследование интерфейса классом
// тело класса
>
Примечание: при переопределении методов интерфейса ключевое слово override использовать не надо.
Если мы создаем объект типа класса, то нам приходится работать с ссылкой на него, что накладывает некоторое дополнительное время обработки. В С++ структура и класс v почти одно и то же, за исключением разницы в доступе по умолчанию и некоторых синтаксических аспектов. В C# структура синтаксически схожа с классом, может инициализироваться также, как и класс, но она имеет тип значения, а не ссылки, как класс. Это обеспечивает повышение эффективности. Итак, когда следует использовать структуры? Их следует использовать в том случае, если объект не обладает особой сложностью и большими объемами. Иначе говоря, структуры следует использовать для создания более простых объектов. Но синтаксически они мало чем отличаются. Для создания структуры используется старое доброе ключевое слово struct. Но теперь структура определяет тип данных, а не тип структуры, как в С++, и необходимость в использовании typedef отпадает. Вообще C# освобождает кодера от всяких таких мелочей, и не только мелочей, работа стала легче. Но это не значит, что это язык для ламеров. Структура в C# не может определять деструкторы, также не может определять конструторы по умолчанию (без параметров).
Итак, ключевое слово class определяет тип ссылки, а struct v тип значения. Пример структуры я приводить не буду, т.к. сейчас это уже предельно просто.
Теперь, наверное, последняя структура данных
— перечисления. В C# под перечислением понимают некоторый упорядоченный набор целочисленных констант. Для создания используется ключевое слово enum. Рассмотрим следующий пример:
доступ к элементам производится как и в случае с классом, но им автоматически присваиваются такие значения:
CodeMen.gates =0
CodeMen.torwald =1
CodeMen.straustrup =2
CodeMen.ritchie =3
CodeMen.kernigan =4
Т.е. они индексируются. Если мы хотим обрабатывать перечисление в цикле, то управляющая переменная должна иметь тип данного перечисления и менять свое значение от значения первого элемента к значению последнего элемента, или наоборот. Обычно перечисления объявляются внутри тела класса. По дефолту, перечисления основываются на типе int, но тип можно указать явно так:
enum CodeMen:ulong;
Теперь все члены перечисления будут иметь значение типа ulong. Это может быть любой другой целочисленный тип, кроме char.
В принципе, теперь вы знаете основы объектно-ориентированной модели C#.
В завершении я хочу привести фундаментальные типы C# с именами классов, которые отвечают за их обработку.
Приведение типов осуществляется, как в С++. Покажем это на следующем примере:
.
int k=10;
object ob1;
ob1=(object)k;
//приведение значения типа int типу object.
.
И, наконец, немного о полиморфизме. Полиморфизм обеспечивается использованием интерфейсов. Хотя, в принципе, полиморфизм можно создать и без интерфейса, но это считается неправильно, типа, если есть — юзай! Я считаю, что на этом не стоит сейчас останавливаться, вы это еще не раз увидите в моих, и не только моих примерах.
Теперь почти все самое нудное позади, далее пойдут вещи новые и более интересные. В следующей статье будет описана перегрузка методов и операторов (только это,
пожалуй, нам не ново), некоторые классы .Net Framework, а также некоторые принципы автоматизации работы программ при помощи того же волшебного .Net. Скоро вы узнаете, почему он волшебный!