Все интерфейсы, реализованные этим типом
Поможем в ✍️ написании учебной работы
Поможем с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой

Объявление типа

Программы на языке C# используют объявление типа для создания нового типа.

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

Пример, объявление класса.

объявление класса это объявление типа, для создания нового класса.

class-declaration:
attributesopt class-modifiersopt partialopt class identifier type-parameter-listopt
            class-baseopt type-parameter-constraints-clausesopt class-body ;opt

opt – необязательно

class identifier class-body - обязательно указывать при определении класса.

Объявление типа включает следующие сведения:

· attributes – все определенные в типе атрибуты;

· class-modifiers – видимость типа;

· Ключевое слово, определяющее тип. В данном примере это class;

· identifier – имя типа;

· type-parameter-list  – список параметров типа;

· class-base базовый тип данного типа;

· type-parameter-constraints-clauses – ограничения параметров типа

· class-body - тело типа.

 

Атрибуты предоставляют дополнительные пользовательские метаданные.

Видимость типа и модификаторы доступа к члену типа. При помощи модификаторов доступа можно задать следующие пять уровней доступности:

Объявленная доступность Значение
public Доступен всем методам во всех сборках.
protected Доступен только методам в определяющем типе и вложенных в него типах или одном из его производных типов независимо от сборки
internal Доступен только методам в определяющей сборке.
protected internal Доступ ограничен текущей сборкой или типами, которые являются производными от содержащего класса.
private(закрытый) Доступен только методам в определяющем типе и вложенных в него типах.

Ключевое слово, определяющее тип.  

Например, class, struct, interface, enum, delegate.

Имя типа. Система общих типов накладывает на имена два следующих ограничения:

Все имена кодируются в виде строк, состоящих из символов Юникода (16-разрядная кодировка).

Имена не могут иметь внедренное (16-разрядное) значение 0x0000.

Список параметров типа. Классы, структуры, интерфейсы и делегаты могут быть определены как пользовательские универсальные типы (универсальные шаблоны).

Пример

public class GenericList<T>

 <T> – type-parameter-list (список параметров типа).

Базовый тип данного типа. Тип может наследовать значения и поведение другого типа. Система общих типов не разрешает наследование от нескольких базовых типов. Все типы являются производными от System.Object, что позволяет:

сравнивать два экземпляра

получать хэш-код экземпляра

определять тип экземпляра

получать копию экземпляра

получать состояние объекта в виде строки



Все интерфейсы, реализованные этим типом.

Ограничения параметров типа (type-parameter-constraints-clauses).

Тело типа. Например, в классе, в теле класса задаются данные и поведение. Поля, свойства, методы и события в классе обозначаются термином члены класса.

Члены типа.

В типе можно определить следующие члены: (Джеффри Рихтер CLR via C#. Программирование на платформе Microsoft .NET Framework 2.0 на языке C# Питер, Русская Редакция 2007)

Константа. Идентификатор, определяющий постоянную величину. Константы связаны с типом, а не с экземпляром.

Поле. Представляет неизменяемое или изменяемое значение. Поле может быть статическим – тогда оно является частью типа и экземплярным ­ ­­­- тогда оно является частью объекта.

Метод. Представляет собой функцию, выполняющую операции, которые изменяют или запрашивают состояние типа (статический метод) или объекта (экземплярный метод).

Конструктор экземпляра– метод, используемый для инициализации полей экземпляра при его создании.

Конструктор типа – метод, используемый для инициализации статических полей типа.

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

Перегруженный оператор.

Оператор преобразования.

Событие.

Тип. Позволяет определять другие вложенные в него типы.

Объявления для типов:

Class declarations

class-declaration:
attributesopt class-modifiersopt partialopt class identifier type-parameter-listopt
            class-baseopt type-parameter-constraints-clausesopt class-body ;opt



Struct declarations

struct-declaration:
attributesopt struct-modifiersopt partialopt struct identifier type-parameter-listopt
            struct-interfacesopt type-parameter-constraints-clausesopt struct-body ;opt



Interface declarations

interface-declaration:
attributesopt interface-modifiersopt partialopt interface identifier type-parameter-listopt
            interface-baseopt   type-parameter-constraints-clausesopt interface-body ;opt



Enum declarations

enum-declaration:
attributesopt enum-modifiersopt enum identifier enum-baseopt enum-body ;opt


Delegate declarations

delegate-declaration:
attributesopt delegate-modifiersopt delegate return-type identifier type-parameter-listopt
            ( formal-parameter-listopt ) type-parameter-constraints-clausesopt ;

Ключевые слова, которые влияют на определение типа или члена типа:

Ключевое слово Тип Метод/Свойство/Событие Константа/Поле
abstract экземпляры не могут быть созданы Необходимо переопределить и реализовать в производном типе – только после этого можно создавать экземпляры производного типа Запрещено
virtual Запрещено Член можно переопределить в производном типе Запрещено
override запрещено Член переопределяется в производном типе Запрещено
sealed Не может использоваться как базовый тип Член нельзя переопределить в производном типе. Применяется к методу, переопределяющему виртуальный метод. Запрещено
new

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

Модификаторы (modifier) типов.

class-modifier struct-modifier: interface-modifier enum-modifier: delegate-modifier:
new public protected internal private abstract sealed static new public protected internal private new public protected internal private   new public protected internal private new public protected internal private

                                                                                                 

Члены (member) типов.

class-member struct -member interface -member enum -member delegate -member
constant field method property event indexer operator constructor destructor static-constructor type constant field method property event indexer operator constructor   static-constructor type     Члены структур не могут использоваться с модификатором protected, потому что наследование от структуры невозможно.       interface-method interface-property interface-event interface-indexer   Члены интерфейса всегда являются открытыми, поскольку целью интерфейса является предоставление возможности доступа к классу или структуре другим типам. attributesopt identifier = constant-expression   Члены перечисления всегда являются открытыми, и модификаторы доступа не применяются  


Классы .

(Из спецификации ) A class is a data structure that may contain data members (constants and fields), function members (methods, properties, events, indexers, operators, instance constructors, destructors and static constructors), and nested types. Class types support inheritance, a mechanism whereby a derived class can extend and specialize a base class.

 

Класс — это структура данных, которая может содержать члены данных (константы и поля), члены функций (методы, свойства, события, индексаторы, операторы, конструкторы экземпляров, деструкторы и статические конструкторы) и вложенные типы. Типы классов поддерживают наследование — механизм, в котором производный класс может расширять и сужать базовый класс

 

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

Class declarations

class-declaration:
attributesopt class-modifiersopt partialopt class identifier type-parameter-listopt
            class-baseopt type-parameter-constraints-clausesopt class-body ;opt

 

Объявление класса состоит из необязательного набора атрибутов, затем следует необязательный набор модификаторов класса, необязательный модификатор partial, ключевое слово class и идентификатор, именующий класс, затем следует необязательный список параметров типа, необязательная спецификация базы класса, необязательный набор предложений ограничений параметров типа, тело класса и необязательная точка с запятой.

Объявление класса не предоставляет предложения ограничений параметров типа, если не предоставляется список параметров типа.

Объявление класса, предоставляющее список параметров типа, является объявлением универсального класса.



Модификаторы класса.

Объявление класса может включать последовательность модификаторов класса:

new
public
protected
internal
private
abstract
sealed

static
Появление в объявлении класса одного и того же модификатора несколько раз является ошибкой времени компилирования. Модификатор new допускается во вложенных классах. Модификаторы public, protected, internal и private управляют доступностью класса. В зависимости от контекста, в котором возникает объявление класса, некоторые из данных модификаторов могут быть запрещены. По умолчанию модификатором класса является модификатор internal. Пример.

using System;

 

namespace ConsoleApplication11

{

class myclass

{

 

}

class Program

{

   static void Main(string[] args)

   {

       myclass obj = new myclass();

   }

}

}

 

Пример. Объявление класса с модификатором private (protected). Данный код приведет к ошибке:

using System;

 

namespace ConsoleApplication11

{

private class myclass

{

 

}

class Program

{

   static void Main(string[] args)

   {

       myclass obj = new myclass();

   }

}

}

Ошибка     1     Элементы, определенные в пространстве имен, не могут объявляться в явном виде как частные, защищенные или защищенные внутренние           

Следующий код не приведет к ошибке. Здесь класс myclass объявленный с модификатором private является вложенным классом.

Using System;

 

namespace ConsoleApplication11

{

class Program

{

   private class myclass

   {

 

   }

   static void Main(string[] args)

   {

       myclass obj = new myclass();

   }

}

}

 

 








Абстрактные классы .

Модификатор abstract используется для указания незавершенности класса и необходимости его использования только в качестве базового класса. Использование оператора new для создания экземпляра  абстрактного класса является ошибкой времени компилирования.

Пример. Следующий код приведет к ошибке: 

using System;

 

namespace ConsoleApplication11

{

   

class Program

{

   abstract class myclass

   {

 

   }

   static void Main(string[] args)

   {

       myclass obj = new myclass();

   }

}

}

Ошибка 1 Невозможно создать экземпляр абстрактного класса или интерфейса.

Можно иметь переменные и значения, имеющие типы времени компиляции, являющиеся абстрактными типами, такие переменные и значения будут обязательно иметь значение null или будут содержать ссылки на экземпляры неабстрактных классов, производных от абстрактных типов.

Пример.

Нельзя создать экземпляр абстрактного класса Brush

Код

Brush mybrash = new Brush();

Приведет к ошибке.

Можно создать переменную типа Brush, которая будет содержать ссылку, например, на экземпляр неабстрактного класса SolidBrush, производного от абстрактного класса Brush:

 

 

 

private void Form1_Paint(object sender, PaintEventArgs e)

{

Brush mybrash = new SolidBrush(Color.Chocolate);

Graphics g = e.Graphics;

g.FillEllipse(mybrash, 100, 100, 30, 30);

}

 

Если неабстрактный класс является производным от абстрактного класса, неабстрактный класс должен включать фактические реализации всех унаследованных абстрактных членов, таким образом переопределяя данные абстрактные члены. Рассмотрим следующий пример:

using System;

 

namespace ConsoleApplication12

{

abstract class myCar

{

   abstract public void DescribeCar();

}

   

class Car : myCar

{

   public override void DescribeCar()

   {

       System.Console.WriteLine(“Four wheels and an engine.”);

   }

}

 

class TestClass

{

   static void Main()

   {

       Car car1 = new Car();

       car1.DescribeCar();

       System.Console.WriteLine(«----------«);

 

   }

}

}

 

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

Класс Car переопределяет DescribeCar и обеспечивает фактическую реализацию. Так как в классе Car нет абстрактных членов, класс Car может быть (но не должен) быть абстрактным.

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

Запечатанные классы .

Модификатор sealed используется для предотвращения создания из него производных классов. Если запечатанный класс указывается в качестве базового класса другого класса, возникает ошибка времени компилирования. Пример:

class A { }

sealed class B : A { }

class C : B { }

Ошибка     1     нельзя наследовать от запечатанного типа

Запечатанный класс не может быть также абстрактным классом.

Модификатор sealed в первую очередь используется для предотвращения непреднамеренного создания из него производных классов. Так как запечатанный класс не имеет производных классов, можно преобразовать вызовы виртуальной функции-члена экземпляров запечатанного класса в невиртуальные вызовы. Пример:

class X

{

   protected virtual void F() { Console.WriteLine("X.F"); }

   protected virtual void F2() { Console.WriteLine("X.F2"); }

}

class Y : X

{

   sealed protected override void F() { Console.WriteLine("Y.F"); }

   protected override void F2() { Console.WriteLine("X.F3"); }

}

class Z : Y

{

   // Ошибка 1 : невозможно переопределить наследуемый член поскольку он запечатан

   // protected override void F() { Console.WriteLine("C.F"); }

   // Overriding F2 is allowed.

   protected override void F2() { Console.WriteLine("Z.F2"); }

}

Статические классы .

 

Класс можно определить с ключевым словом static

Не реализует никаких интерфейсов.

Можно определять только статические члены.

Статический класс не может содержать члены с объявленной доступностью protected или protected internal.

Нельзя создавать экземпляры статического класса.

Доступ к членам статического класса осуществляется с использованием имени класса

Класс нельзя использовать в качестве параметра метода или локальной переменной.

Статический класс не может содержать модификатор sealed или abstract.

Нарушение данных ограничений приводит к ошибке времени компилирования.

Статический класс не имеет конструкторов экземпляров.

Члены статического класса автоматически не являются статическими, а объявления членов должны явно включать модификатор static (исключая константы и вложенные типы).

Тело класса

Тело класса для класса определяет члены этого класса.

class-body:
{ class-member-declarationsopt }

тело_класса:
{ объявления_члена_классанеобязательно }



Члены класса.

Члены типа класса разделены на следующие категории (спецификация)

· Константы, представляющие постоянные значения, связанные с классом.

· Поля, являющиеся переменными класса.

· Методы, реализующие вычисления и действия, которые могут быть выполнены классом.

· Свойства, определяющие именованные характеристики и действия, связанные с чтением и записью данных характеристик.

· События, определяющие уведомления, которые могут быть сгенерированы классом.

· Индексаторы, которые обеспечивают индексацию экземпляров класса аналогично (синтаксически) индексации массивов.

· Операторы, определяющие операторы выражений, которые могут быть применены к экземплярам класса.

· Конструкторы экземпляров, реализующие действия, требуемые для инициализации экземпляров класса.

· Деструкторы, реализующие действия, выполняемые до отмены экземпляров класса без возможности восстановления.

· Статические конструкторы, реализующие действия, требуемые для инициализации самого класса.

· Типы, представляющие локальные типы класса.

Все члены, которые могут содержать исполняемый код, считаются членами функций типа класса. Членами функций типа класса являются методы, свойства, события, индексаторы, операторы, конструкторы экземпляров, деструкторы и статические конструкторы данного типа класса.

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

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

 

Члены класса являются либо статическими членами, либо членами экземпляра. В общем случае считается, что статические члены принадлежат к типам классов, а члены экземпляра принадлежат к объектам (экземплярам типов классов).

Каждый экземпляр класса содержит отдельный набор всех полей экземпляра класса.

Член функции экземпляра (метод, свойство, индексатор, конструктор экземпляра или деструктор) работает на заданном экземпляре класса, и данный экземпляр может быть вызван в качестве this.\

Класс может содержать объявления следующих членов:

class-member: Члены класса declaration
constant Константа   attributesopt constant-modifiersopt const type constant-declarators ;
field Поле static and instance fields attributesopt field-modifiersopt type variable-declarators ;    атрибутынеоб модификаторы_полянеоб тип деклараторы_переменных ;
method Метод method-header: attributesopt method-modifiersopt partialo pt return-type member-name type-parameter-listopt                    ( formal-parameter-listopt ) type-parameter-constraints-clausesopt public static void OpTest<T>(T s, T t)
property Свойство property-declaration: attributesopt property-modifiersopt type member-name { accessor-declarations } get-accessor-declaration: attributesopt accessor-modifieropt    get accessor-body set-accessor-declaration: attributesopt accessor-modifieropt set accessor-body
event Событие attributesopt event-modifiersopt event type variable-declarators ; public event EventHandler Click;
indexer Индексатор indexer-declaration: attributesopt indexer-modifiersopt indexer-declarator { accessor-declarations } indexer-declarator: type this [ formal-parameter-list ] type interface-type . this [ formal-parameter-list ]
operator Операторы attributesopt operator-modifiers operator-declarator operator-body
constructor Конструктор экземпляра attributesopt constructor-modifiersopt constructor-declarator constructor-body
destructor деструктор attributesopt externopt ~ identifier ( ) destructor-body
static-constructor Конструктор типа static-constructor-declaration: attributesopt static-constructor-modifiers identifier ( ) static-constructor-body
type типы  

 

 

 

Конструкторы экземпляров .

Конструктор экземпляра является членом, реализующим действия, необходимые для инициализации экземпляра класса. Конструкторы экземпляров объявляются с помощью объявлений конструкторов:

constructor-declaration:
attributesopt constructor-modifiersopt constructor-declarator constructor-body

объявление_конструктора:
атрибутынеобязательно модификаторы_конструкторанеобязательно декларатор_конструктора тело_конструктора

модификатор_конструктора:
public
protected
internal
private
extern

декларатор_конструктора:
идентификатор ( список_формальных_параметровнеобязательно ) инициализатор_конструкторанеобязательно

инициализатор_конструктора:
: base ( список_аргументовнеобязательно )
: this ( список_аргументовнеобязательно )

тело_конструктора:
блок
;

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

 

Необязательный список формальных параметров конструктора экземпляров подчиняется тем же правилам, что и список формальных параметров метода. Список формальных параметров определяет подпись конструктора экземпляров и управляет процессом, посредством которого разрешение перегрузки выбирает отдельный конструктор экземпляров в вызове.

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

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

Конструкторы экземпляров не наследуются.

Если класс не содержит объявления конструкторов экземпляров, автоматически предоставляется конструктор экземпляров по умолчанию.













Выполнение конструктора

Инициализаторы переменных преобразуются в операторы присваивания, и эти операторы присваивания выполняются перед вызовом конструктора экземпляров базового класса. Этот порядок обеспечивает инициализацию всех полей экземпляра их инициализаторами переменных до выполнения каких-либо операторов, имеющих доступ к этому экземпляру.

Пример.

 

using System;

 

namespace ConsoleApplication12

{

   

class A

{

   public A()

   {

       PrintFields();

   }

   public virtual void PrintFields() { }

}

class B : A

{

   int x = 1;

   int y;

   public B()

   {

       y = -1;

   }

   public override void PrintFields()

   {

       Console.WriteLine("x = {0}, y = {1}", x, y);

   }

}

 

class Program

{

       

   static void Main(string[] args)

   {

       B b = new B();

   }

}

}

 

В этом примере, если new B() используется для создания экземпляра B, создается следующий вывод:

x = 1, y = 0

 

Пример

using System;

using System.Collections;

using System.Linq;

using System.Text;

 

namespace ConsoleApplication1

{

class A

{

   int x = 1, y = -1, count;

   public A()

   {

       count = 0;

   }

   public A(int n)

   {

       count = n;

   }

}

class B : A

{

   double sqrt2 = Math.Sqrt(2.0);

   ArrayList items = new ArrayList(100);

   int max;

   public B()

       : this(100)

   {

       items.Add("default");

   }

   public B(int n)

       : base(n - 1)

   {

       max = n;

   }

}

 

 

class Program

{

 

   static void Main(string[] args)

   {

       B b = new B();

       B bb = new B(5);

   }

}

}

 

В этом примере содержится несколько инициализаторов переменных, а также содержатся инициализаторы конструкторов обоих видов (base и this).

 

Конструкторы по умолчанию

Конструктор по умолчанию создается автоматически если нет явно заданных конструкторов.

Конструкторы экземпляров для ссылочного типа создаются с модификатором public. Тип может определять несколько конструкторов с различными сигнатурами.

Закрытые конструкторы

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

public class Trig
{
private Trig() {}  // Prevent instantiation

public const double PI = 3.14159265358979323846;

public static double Sin(double x) {...}
public static double Cos(double x) {...}
public static double Tan(double x) {...}
}

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






Статические конструкторы

Статический конструктор – это член, реализующий действия, необходимые для инициализации закрытого типа класса. Статические конструкторы объявляются с помощью объявлений_статического_конструктора:

объявление_статического_конструктора:
атрибутынеобязательно модификаторы_статического_конструктора идентификатор  ( ) тело_статического_конструктора

модификаторы_статического_конструктора:
externнеобязательно static
static externнеобязательно

тело_статического_конструктора:
блок
;

Объявление_статического_конструктора может включать набор атрибутов и модификатор extern.

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

Если в объявление статического конструктора включен модификатор extern, статический конструктор называется внешним статическим конструктором. Так как объявление внешнего статического конструктора не предоставляет фактическую реализацию, его тело_статического_конструктора состоит из точки с запятой. Для всех других объявлений статического конструктора тело_статического_конструктора состоит из блока, в котором указаны операторы, которые необходимо выполнить, чтобы инициализировать класс.

Статические конструкторы не наследуются и их нельзя вызвать непосредственно.

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

· создан экземпляр типа класса;

· возникла ссылка на любой статический член типа класса.

Если класс содержит метод Main, в котором начинается исполнение, статический конструктор для этого класса выполняется до вызова метода Main.

Для инициализации нового закрытого типа класса сначала создается новый набор статических полей для этого особого типа класса. Каждое из статических полей инициализируется своим значением по умолчанию . Затем выполняются инициализаторы этих статических полей . Наконец выполняется статический конструктор.

 

Пример.

using System;

 

namespace ConsoleApplication12

{

  

 class SimpleClass

{

// Static variable that must be initialized at run time.

static readonly long baseline;

 

// Static constructor is called at most one time, before any

// instance constructor is invoked or member is accessed.

static SimpleClass()

{

   baseline = DateTime.Now.Ticks;

}

}

 

class Program

{

       

   static void Main(string[] args)

   {

      SimpleClass scl = new SimpleClass();

           

   }

}

}

 

Статический конструктор не принимает модификаторы доступа и не имеет параметров.

Статический конструктор вызывается автоматически для инициализации класса перед созданием первого экземпляра или ссылкой на какие-либо статические члены.

Статический конструктор нельзя вызывать напрямую.

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






Создание конструктора копии

В отличие от некоторых языков в C# отсутствует конструктор копии. Если создан новый объект и требуется скопировать значения из существующего объекта, необходимо создать соответствующий метод самостоятельно. Пример (msdn).

class Person{ private string name; private int age; // Copy constructor. public Person(Person previousPerson) {   name = previousPerson.name;   age = previousPerson.age; } // Instance constructor. public Person(string name, int age) {   this.name = name;   this.age = age; } // Get accessor. public string Details {   get   {       return name + " is " + age.ToString();   } }} class TestPerson{ static void Main() {   // Create a new person object.   Person person1 = new Person("George", 40);    // Create another new object, copying person.   Person person2 = new Person(person1);   Console.WriteLine(person2.Details);    // Keep the console window open in debug mode.      Console.WriteLine("Press any key to exit.");   Console.ReadKey(); }}

Константы

Константа – это член класса, представляющий постоянное значение: значение, которое может быть вычислено во время компиляции. Объявление_константы

объявление_константы:
атрибутынеоб модификаторы_константнеоб const тип деклараторы_констант ;

модификатор_константы:
new
public
protected
internal
private

деклараторы_констант:

 

идентификатор = выражение_константы

Константы считаются статическими членами, объявление_константы не требует и не допускает модификатор static.

Тип, указанный в объявлении константы, должен быть типом sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string.

Пример const локальная переменная

using System;

class Program

{

static void Main(string[] args)

{

   const int months = 12;

   Console.WriteLine(months);

}

}

 

Пример const поле

 

using System;

class Calendar

{

public const int months = 12;

}

class Program

{

static void Main(string[] args)

{

   int birthstones = Calendar.months;

   Console.WriteLine(birthstones);

}

}







Поля .

Поле – это член, представляющий переменную, связанную с объектом или классом. Объявление_поля вводит одно или более полей заданного типа.

объявление_поля:
атрибутынеоб модификаторы_полянеоб тип деклараторы_переменных ;

модификатор_поля:
new
public
protected
internal
private
static
readonly
volatile

декларатор_переменной:
идентификатор = инициализатор_переменной

инициализатор_переменных:
выражение
инициализатор_массива

Объявление поля может включать модификатор readonly или volatile, но не оба. Ключевое слово volatile указывает, что поле может быть изменено несколькими потоками, выполняющимися одновременно.

Декларатор переменной состоит из идентификатора, именующего член, за которым необязательно следуют лексема «=» и инициализатор переменной, задающий начальное значение члена.

Тип поля должен быть, по крайней мере, таким же доступным, как само поле.

Значение поля получается в выражении с помощью простого имени или доступа к члену.

Значение поля с доступом не только для чтения изменяется с помощью присваивания.

Значение поля с доступом не только для чтения можно и получать, и изменять с помощью операторов постфиксного увеличения и уменьшения и операторов префиксного увеличения и уменьшения.

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

class A
{
public static int X = 1, Y, Z = 100;
}

эквивалентно

class A
{
public static int X = 1;
public static int Y;
public static int Z = 100;
}





















Поля только для чтения .

Если объявление_поля включает модификатор readonly, поля, введенные этим объявлением, являются полями только для чтения.

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

Например:

public class ReadOnlyTest

{

class SampleClass

{

public int x;

// Initialize a readonly field

public readonly int y = 25;

public readonly int z;

 

public SampleClass()

{

    // Initialize a readonly instance field

    z = 24;

}

 

public SampleClass(int p1, int p2, int p3)

{

    x = p1;

    y = p2;

    z = p3;

}

}

static void Main()

{

SampleClass p1 = new SampleClass(11, 21, 32); // OK

Console.WriteLine("p1: x={0}, y={1}, z={2}", p1.x, p1.y, p1.z);

SampleClass p2 = new SampleClass();

p2.x = 55; // OK

Console.WriteLine("p2: x={0}, y={1}, z={2}", p2.x, p2.y, p2.z);

}

}

Ответ

 

Поле static readonly полезно, если требуется символическое имя для значения константы, но тип значения недопустим в объявлении const, или если значение не может быть вычислено во время компиляции. Пример.

public class Color

{

   public static readonly Color Black = new Color(0, 0, 0);

   public static readonly Color White = new Color(255, 255, 255);

   public static readonly Color Red = new Color(255, 0, 0);

   public static readonly Color Green = new Color(0, 255, 0);

   public static readonly Color Blue = new Color(0, 0, 255);

   private byte r, g, b;

   public Color(byte r, byte g, byte b)

   {

       this.r = r;

       this.g = g;

       this.b = b;

   }

}

class Test

{

   static void Main()

   {

       Color c = Color.Black;

         

       Console.ReadKey();

   }

}

Инициализация поля .

Начальным значением поля, как статического, так и поля экземпляра, является значение по умолчанию типа поля. Невозможно видеть значение поля до выполнения этой инициализации по умолчанию, и поэтому поле никогда не бывает «неинициализированным». Пример.

using System;

class Test
{
static bool b;
int i;

static void Main() {
       Test t = new Test();
       Console.WriteLine("b = {0}, i = {1}", b, t.i);
}
}

В этом примере производятся следующие выходные данные

b = False, i = 0

так как и b, и i автоматически инициализированы значениями по умолчанию.

Объявления полей могут включать инициализаторы_переменных. Для статических полей инициализаторы переменных соответствуют операторам присваивания, выполняемым во время инициализации класса. Для полей экземпляров инициализаторы переменных соответствуют операторам присваивания, выполняемым при создании экземпляра класса.

Пример.

using System;

class Test
{
static double x = Math.Sqrt(2.0);
int i = 100;
string s = "Hello";

static void Main() {
       Test a = new Test();
       Console.WriteLine("x = {0}, i = {1}, s = {2}", x, a.i, a.s);
}
}
















Методы

Метод – это член, реализующий вычисление или действие, которое может быть выполнено объектом или классом. Методы объявляются с помощью объявлений_методов:

объявление_метода:
заголовок_метода тело_метода

заголовок_метода:
атрибутынеобязательно модификаторы_методанеобязательно partialнеобязательно тип_возвращаемого_значения имя_элемента список_параметров_типанеобязательно
            ( список_формальных_параметровнеобязательно ) предложения_ограничений_параметров_типанеобязательно

модификатор_метода:
new
public
protected
internal
private
static
virtual
sealed
override
abstract
extern

возвращаемый_тип:
тип
void

имя_элемента:
идентификатор
тип_интерфейса . идентификатор

тело_метода:
блок
;

Объявление_метода может включать набор атрибутов и допустимое сочетание из четырех модификаторов доступа, и модификаторы new, static, virtual, override, sealed, abstract и extern.

Объявление имеет допустимое сочетание модификаторов, если верно все следующее:

· объявление включает допустимое сочетание модификаторов доступа;

· объявление не включает один и тот же модификатор несколько раз;

· объявление включает не более одного из следующих модификаторов: static, virtual и override;

· объявление включает не более одного из следующих модификаторов: new и override;

· если объявление включает модификатор abstract, то объявление не включает ни один из следующих модификаторов: static, virtual, sealed и extern;

· если объявление включает модификатор private, то объявление не включает ни один из следующих модификаторов: virtual, override и abstract;

· если объявление включает модификатор sealed, то объявление также включает модификатор override;

· если объявление включает модификатор partial, то оно не включает ни один из следующих модификаторов: new, public, protected, internal, private, virtual, sealed, override, abstract и extern.

Тип_возвращаемого_значения в объявлении метода указывает тип значения, вычисляемого и возвращаемого методом. Если метод не возвращает значение, типом_возвращаемого_значения является void.

Имя_члена указывает имя метода. Если только метод не является членом явной реализации интерфейса, имя_члена – это просто идентификатор. Для члена явной реализации интерфейса имя_члена состоит из типа_интерфейса, за которым следует «.» и идентификатор.

Необязательный список_формальных_параметров указывает параметры метода.

Для методов abstract и extern тело_метода состоит просто из точки с запятой.

Сигнатура метода состоит из его имени, числа параметров типов и числа, модификаторов и типов его формальных параметров.

Имя метода должно отличаться от имен всех других не методов, объявленных в этом же классе. Кроме того, подпись метода должна отличаться от подписей всех других методов, объявленных в этом же классе, а два метода, объявленные в одном и том же классе, не могут иметь подписи, отличающиеся только параметрами ref и out.

Все формальные параметры и параметры типов должны иметь разные имена.





















Параметры метода

Параметры метода, если они имеются, объявляются списком формальных параметров метода.

список_формальных_параметров:

фиксированные_параметры , массив_параметров

 

фиксированный_параметр:
атрибутынеобязательно модификатор_параметранеобязательно тип идентификатор аргумент_по_умолчаниюнеобязательно

аргумент_по_умолчанию:
= выражение

модификатор_параметра:
ref
out
this

массив_параметров:
атрибутынеобязательно params тип_массива идентификатор

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

В примере демонстрируются разные виды параметров.

public void M(
ref int i,
decimal d,
bool         b = false,
bool?   n = false,
string  s = "Hello",
object  o = null,
T       t = default(T),
params int[] a
) { }

В списке_формальных_параметров для M, i является обязательным параметром, d является обязательным параметром по значению, b, s, o и t являются необязательными параметрами, и a является массивом параметров.

 

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

Есть четыре вида формальных параметров:

· параметры по значению, объявляемые без модификаторов;

· параметры по ссылке, объявляемые с модификатором ref;

· выходные параметры, объявляемые с модификатором out;

· массивы параметров, объявляемые с модификатором params.
















Параметры по значению

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

Если формальный параметр является параметром по значению, соответствующий аргумент в вызове метода должен быть выражением, неявно преобразуемым в тип формального параметра.

Параметры по ссылке

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

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

Внутри метода параметр по ссылке всегда считается определенно назначенным.

Выходные параметры

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

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

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

Каждый выходной параметр метода должен быть определенно назначен до возврата из метода.

Примеры.

Виртуальные методы

Если объявление метода экземпляра содержит модификатор virtual, метод является виртуальным методом. Если модификатор virtual отсутствует, метод называется невиртуальным методом.

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

В следующем примере показаны различия между виртуальными и невиртуальными методами.

using System;

class A
{
public void F() { Console.WriteLine("A.F"); }

public virtual void G() { Console.WriteLine("A.G"); }
}

class B: A
{
new public void F() { Console.WriteLine("B.F"); }

public override void G() { Console.WriteLine("B.G"); }
}

class Test
{
static void Main() {
       B b = new B();
       A a = b;
       a.F();
       b.F();
       a.G();
       b.G();
}
}

















Переопределяющие методы

Запечатанные методы

Если объявление метода экземпляра содержит модификатор sealed, метод является запечатанным методом. Если объявление метода экземпляра включает модификатор sealed, оно должно также включать модификатор override. Использование модификатора sealed предотвращает последующее переопределение метода в производном классе.

Абстрактные методы

Если объявление метода экземпляра содержит модификатор abstract, метод является абстрактным методом. Хотя абстрактный метод неявно является также виртуальным методом, он не может иметь модификатора virtual.

Объявление абстрактного метода представляет новый виртуальный метод, но не предоставляет его реализацию. Вместо этого необходимо предоставить неабстрактные производные классы, предоставляющие собственные реализации этого метода посредством его переопределения. Поскольку абстрактный метод не предоставляет фактическую реализацию, тело_абстрактного_метода состоит только из точки с запятой.

Тело метода

Тело_метода содержит блок или точку с запятой.

Объявления абстрактных и внешних методов не предоставляют реализацию метода, поэтому их тела содержат только точку с запятой. Тело любого другого метода содержит блок, включающий в себя операторы, которые выполняются при вызове метода.

В теле метода, который имеет тип возвращаемого значения void, не допускается использование операторов return для задания выражений.

Если метод возвращает значение, тип которого отличается от void, каждый оператор return в теле этого метода должен задавать выражение, неявно преобразуемое к типу возвращаемого значения.

 

1. Пользовательские преобразования .

Ключевое слово implicit служит для объявления неявного оператора преобразования пользовательского типа. Чтобы выполнить эти преобразования, тип должен определять:

открытые конструкторы с одним параметром;

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

 

Каждый такой метод преобразует экземпляр типа, в котором определен этот метод, в экземпляр другого типа.

 

Пример. Преобразование значения типа Digit к целочисленному типу Byte

Перегрузка операторов.

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

К унарным операторам, допускающим перегрузку, относятся:

+ - ! ~ ++ -- true false

К бинарным операторам, допускающим перегрузку, относятся:

+ - * / % & | ^ << >> == != > < >= <=

Невозможна перегрузка операторов доступа к членам, вызова методов или операторов =, &&, ||, ??, ?:, =>, checked, unchecked, new, typeof, default, as и is.

При перегрузке бинарного оператора связанный оператор присваивания (если он есть) также неявно перегружается. Например, при перегрузке оператора * также выполняется перегрузка оператора *=. Оператор присваивания ( = ) нельзя перегрузить. При присваивании всегда происходит простое побитовое копирование значения в переменную.

Перегрузка операций приведения типов, например (T)x, осуществляется путем предоставления пользовательских преобразований.

Доступ к элементу, например a[x], не считается оператором, допускающим перегрузку.

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

Стандартные операторы сложения:

int operator +(int x, int y);
uint operator +(uint x, uint y);
long operator +(long x, long y);
ulong operator +(ulong x, ulong y);

у пользовательского оператора не может быть такой же сигнатуры, что и у стандартного оператора, поэтому пример работать не будет:

using System;

namespace ConsoleApplication3

{

class Program

{

  public static int operator +(int a, int b)

   {

       return a + b;

   }

   static void Main(string[] args)

   {

       Int32 a = 5;

       Int32 b = 6;

       Int32 c = a + b;

       Console.WriteLine(c);

   }

}

}

Ошибка. Тип одного из параметров бинарного оператора должен быть вмещающим

Пример.

Типы, определенные пользователем, могут вызвать перегрузку оператора +.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace ConsoleApplication1

{

class myint

{

   int a;

   public myint(int a)

   {

       this.a = a;

   }

   public static myint operator +(myint a, myint b)

   {

       return new myint(a.a + b.a);

   }

   public override string ToString()

   {

       return String.Format("{0}", a);

   }

 

}

 

class Program

{

   static void Main(string[] args)

   {

       myint a = new myint(9);

       myint b = new myint(9);

       myint c = a + b;

       Console.WriteLine(c);

   }

}

 

}

 

Задание.

Методы перегруженных операторов. Перегруженные операторы – это открытые статические методы. У оператора-метода тип одного из параметров или тип возвращаемого значения должен совпадать с типом, в котором определен оператор-метод. Пример .

public class Complex

{

   double re;

   double im;

   public Complex(double re, double im)

   {

       this.re = re;

       this.im = im;

   }

  public static Complex operator+(Complex c1,Complex c2)

   {

       return new Complex(c1.re + c2.re, c1.im + c2.im);

   }

   public override string ToString()

   {

       return String.Format("{0} {1}i",re, im);

   }

}

class Program

{

   static void Main(string[] args)

   {

      Complex c3 = new Complex(2, 4) + new Complex(7, 1);

      Console.WriteLine(c3.ToString());

   }

}

Задание. Перемножить матрицы.

V = [ 17 3-4j -3 3+4j ]






Свойства .

Свойство — это член, предоставляющий доступ к характеристикам объекта или класса. Как свойства, так и поля являются именованными членами со связанными типами, для обращения к которым используется одинаковый синтаксис. Однако в отличие от полей, свойства не указывают места хранения. Вместо этого свойства содержат методы доступа, определяющие операторы, которые используются при чтении или записи их значений. Таким образом, свойства предоставляют механизмы, позволяющие связать определенные действия с чтением или записью атрибутов объекта. Кроме того, свойства обеспечивают вычисление таких атрибутов.

Свойства объявляются с помощью объявлений_свойств:

объявление_свойства:
атрибутынеобязательно модификаторы_свойствнеобязательно тип имя_члена { объявления_методов_доступа }

модификатор_свойства:
new
public
protected
internal
private
static
virtual
sealed
override
abstract
extern

имя_элемента:
идентификатор
тип_интерфейса . идентификатор

Тип свойства задает представленный в объявлении тип свойства. Имя члена задает имя свойства. Если в свойстве не содержится явная реализация члена интерфейса, имя члена представляет собой просто идентификатор. При явной реализации члена интерфейса имя члена состоит из типа интерфейса, точки «.» и идентификатора (в указанной последовательности).

Объявления методов_доступа заключаются в фигурные скобки «{» и «}» и объявляют методы доступа свойства. Методы доступа задают исполняемые операторы, связанные с чтением или записью свойства.

Несмотря на то, что для обращения к свойствам и полям используется одинаковый синтаксис, свойство не классифицируется как переменная. Поэтому невозможно передать свойство как аргумент ref или out.















Методы доступа

Объявления_методов_доступа свойства задают исполняемые операторы, связанные с чтением или записью свойства.

объявления_методов_доступа:
объявление_метода_доступа_get объявление_метода_доступа_setнеобязательно
объявление_метода_доступа_set объявление_метода_доступа_getнеобязательно

объявление_метода_доступа_get:
атрибутынеобязательно модификатор_метода_доступанеобязательно    get тело_метода_доступа

объявление_метода_доступа_set:
атрибутынеобязательно модификатор_метода_доступанеобязательно    set тело_метода_доступа

модификатор_метода_доступа:
protected
internal
private
protected internal
internal protected

тело_метода_доступа:
блок
;

Объявление метода доступа включает в себя объявление_метода_доступа_get, объявление_метода_доступа_set или оба этих объявления. Каждое объявление метода доступа состоит из маркера get или set, за которым следуют необязательный модификатор метода доступа и тело метода доступа.

Свойство, содержащее оба метода доступа (get и set), называется свойством для чтения и записи.

Методы доступа get и set свойства не являются отдельными членами и не могут объявляться отдельно от свойства.

 


Автоматически реализуемые свойства.  Пример (MSDN).

Автоматически реализуемые свойства способствуют более лаконичному объявлению свойств. Пример.

namespace ConsoleApplication1

{

class Person

{

   public string Name { get; set; }

   public int Age { get; set; }

 

   public override string ToString()

   {

       return "Person: " + Name + " " + Age;

   }

}

class Program

{

   static void Main(string[] args)

   {

       Person person = new Person { Name = "John", Age = 12 };

       Console.WriteLine(person);

 

   }

}

}

В примере используется инициализатор объектов.

Инициализаторы объектов позволяют присваивать значения всем доступным полям и свойствам объекта в момент создания без явного вызова конструктора.

Person person = new Person { Name = "John", Age = 12 };

 

Пример

namespace ConsoleApplication1

{

 

class Person

{

   public string Name { get; set; }

   public int Age { get; set; }

 

   public override string ToString()

   {

       return "Person: " + Name + " " + Age;

   }

}

 

class Persons

{

   Person person = new Person { Name = "John1", Age = 121 };

   string Address = "123 Main St";

   public override string ToString()

   {

       return person.ToString()+" "+Address;

   }

}

class Program

{

   static void Main(string[] args)

   {

       Persons person = new Persons();

       Console.WriteLine(person);

 

   }

}

 

}

 

Для автоматически реализуемых свойств требуются оба метода доступа — get и set. Чтобы сделать класс неизменяемым, методы доступа set следует объявить с модификатором private. Однако в случае объявления закрытого метода доступа set для инициализации свойства нельзя использовать инициализатор объекта. Необходимо использовать конструктор или метод.

В следующем примере показано два способа реализации неизменяемого класса с автоматически реализуемыми свойствами. В первом классе для инициализации свойств используется конструктор, а во втором классе — статический метод.

 

class Contact

{

// Read-only properties.

public string Name { get; private set; }

public string Address { get; private set; }

 

// Public constructor.

public Contact(string contactName, string contactAddress)

{

   Name = contactName;

   Address = contactAddress;

}

}

 

// This class is immutable. After an object is created,

// it cannot be modified from outside the class. It uses a

// static method and private constructor to initialize its properties.  

public class Contact2

{

// Read-only properties.

public string Name { get; private set; }

public string Address { get; private set; }

 

// Private constructor.

private Contact2(string contactName, string contactAddress)

{

   Name = contactName;

   Address = contactAddress;

}

 

// Public factory method.

public static Contact2 CreateContact(string name, string address)

{

   return new Contact2(name, address);

}

}

 

public class Program

{

static void Main()

{

   // Some simple data sources.

   string[] names = {"Terry Adams","Fadi Fakhouri", "Hanying Feng",

                         "Cesar Garcia", "Debra Garcia"};

   string[] addresses = {"123 Main St.", "345 Cypress Ave.", "678 1st Ave",

                             "12 108th St.", "89 E. 42nd St."};

 

 

   // Create Contact objects by using a constructor.

   Contact[] con = new Contact[5];

 

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

   {

       con[i] = new Contact(names[i], addresses[i]);

   }

 

 

   foreach (Contact contact in con)

   {

       Console.WriteLine("{0}, {1}", contact.Name, contact.Address);

   }

   Console.WriteLine();

   Contact2[] con2 = new Contact2[5];

 

   // Create Contact2 objects by using a static factory method.

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

   {

       con2[i] = Contact2.CreateContact(names[i], addresses[i]);

   }

 

   foreach (Contact2 contact in con2)

   {

       Console.WriteLine("{0}, {1}", contact.Name, contact.Address);

   }

 

   Console.WriteLine("Press any key to exit.");

   Console.ReadKey();

}

}

 

 

Пример использования метода вместо открытых конструкторов.

 Array myArr = Array.CreateInstance(typeof(Int32), 3);

Пример использования метода вместо открытых конструкторов.

Graphics g = CreateGraphics();

g.DrawLine(new Pen(Color.AliceBlue, 3.9f), new Point(3, 7), new Point(100, 100));

Индексаторы

 

Индексатор является членом, дающим возможность индексировать объект так же, как массив. Индексаторы объявляются с помощью объявлений_индексаторов:

объявление_индексатора:
атрибутынеоб модификаторы_индексаторанеоб декларатор_индексатора { объявления_методов_доступа }

модификатор_индексатора:
new
public
protected
internal
private
virtual
sealed
override
abstract
extern

декларатор_индексатора:
тип this [ список_формальных_параметров ]
тип тип_интерфейса . this [ список_формальных_параметров ]


























Наследование. Полиморфизм.

 

class A {}

class B: A {}

 

В этом примере класс A считается прямым базовым классом для класса B, а класс B считается производным из класса A. Так как класс A не указывает явно прямой базовый класс, его неявным прямым базовым классом является object.

 

За исключением класса object, каждый тип класса имеет строго один прямой базовый класс. Класс object не имеет прямого базового класса и является окончательным базовым классом для всех других классов.

 

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

Пример.

 

using System;

namespace ConsoleApplication16

{

class BaseClass

{

   public BaseClass()

   {

       Console.WriteLine("Вызван конструктор базового класса");

   }

}   

class DerivedClass : BaseClass

{

   public DerivedClass()

   {

       Console.WriteLine("Вызван конструктор производного класса");

   }

}

class Program

{

   static void Main(string[] args)

   {

       DerivedClass md = new DerivedClass();

   }

}

}

 

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

 

Если в базовом классе определено несколько перегруженных конструкторов необходимо определить конструктор без параметров, если его нет выдается сообщение об ошибке. Для вызова конструктора с параметром необходимо использовать инициализатор конструкторов base.  Пример. Использование base.

 

using System;

namespace ConsoleApplication16

{

public class BaseClass

{

   int num;

   public BaseClass()

   {

       Console.WriteLine("in BaseClass()");

   }

   public BaseClass(int i)

   {

       this.num = i;

       Console.WriteLine("in BaseClass(int i)");

   }

 

   public int GetNum()

   {

       return num;

   }

}

public class DerivedClass : BaseClass

{

   // This constructor will call BaseClass.BaseClass()

   public DerivedClass() : base()

   {

   }

   // This constructor will call BaseClass.BaseClass(int i)

   public DerivedClass(int i) : base(i)

   {

   }

}

class Program

{

   static void Main(string[] args)

   {

       DerivedClass md = new DerivedClass();

       DerivedClass md1 = new DerivedClass(1);

       Console.WriteLine(md1.GetNum());

   }

}

}

Пример

Создать два класса в каждом конструктор и метод

 

using System;

namespace ConsoleApplication17

{

class Point

{

   int x;

   int y;

   public Point(int x, int y)

   {

       this.x = x;

       this.y = y;

   }

   public void Draw()

   {

       Console.WriteLine("Рисование точки в ({0}, {1})", x, y);

   }

}

class Rectangle

{

   int x;

   int y;

   int w;

   int h;

   public Rectangle(int x, int y, int w,int h)

   {

       this.x = x;

       this.y = y;

       this.w = w;

       this.h = h;

   }

   public void Draw()

   {

       Console.WriteLine("Рисование прямоугольника в ({0}, {1})", x, y);

   }

}

class ShapeApp

{

   static void Main(string[] args)

   {

       Rectangle r = new Rectangle(1, 24, 10, 20);

       r.Draw();

       Point p = new Point(10, 25);

       p.Draw();

   }

}

}

 

Создать базовый класс Shape

 

using System;

namespace ConsoleApplication17

{

class Shape

{

   protected int x;

   protected int y;

   public Shape(int x, int y)

   {

       this.x = x;

       this.y = y;

   }

}

class Point : Shape

{

   public Point(int x, int y) : base(x,y)

   {

   }

   public void Draw()

   {

       Console.WriteLine("Рисование точки в ({0}, {1})", x, y);

   }

}

class Rectangle :Shape

{

   int w;

   int h;

   public Rectangle(int x, int y, int w,int h) : base(x,y)

   {

       this.w = w;

       this.h = h;

   }

   public void Draw()

   {

       Console.WriteLine("Рисование прямоугольника в ({0}, {1})", x, y);

   }

}

class ShapeApp

{

   static void Main(string[] args)

   {

       Rectangle r = new Rectangle(1, 24, 10, 20);

       r.Draw();

       Point p = new Point(10, 25);

       p.Draw();

   }

}

}

 

 

Метод Draw класса Shape определим с ключевым словом virtual. Метод Draw производных классов с ключевым словом override

 

using System;

namespace ConsoleApplication17

{

class Shape

{

   protected int x;

   protected int y;

   public Shape(int x, int y)

   {

       this.x = x;

       this.y = y;

   }

   public virtual void Draw()

   {

   }

}

class Point : Shape

{

   public Point(int x, int y) : base(x,y)

   {

   }

   public override void Draw()

   {

       Console.WriteLine("Рисование точки в ({0}, {1})", x, y);

   }

}

class Rectangle :Shape

{

   int w;

   int h;

   public Rectangle(int x, int y, int w,int h) : base(x,y)

   {

       this.w = w;

       this.h = h;

   }

   public override void Draw()

   {

       Console.WriteLine("Рисование прямоугольника в ({0}, {1})", x, y);

   }

}

class ShapeApp

{

   static void Main(string[] args)

   {

       Shape r = new Rectangle(1, 24, 10, 20);

       r.Draw();

       Shape p = new Point(10, 25);

       p.Draw();

   }

}

}

 

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

 

В следующем фрагменте кода реализация виртуального метода Draw базового класса Shape заменяется реализацией метода Draw производного класса.

 

class Shape

{

   protected int x;

   protected int y;

   public Shape(int x, int y)

   {

       this.x = x;

       this.y = y;

   }

   public virtual void Draw()

   {

   }

}

class Point : Shape

{

   public Point(int x, int y) : base(x,y)

   {

   }

   public override void Draw()

   {

       Console.WriteLine("Рисование точки в ({0}, {1})", x, y);

   }

}

class Rectangle :Shape

{

   int w;

   int h;

   public Rectangle(int x, int y, int w,int h) : base(x,y)

   {

       this.w = w;

       this.h = h;

   }

   public override void Draw()

   {

       Console.WriteLine("Рисование прямоугольника в ({0}, {1})", x, y);

   }

}

class ShapeApp

{

   static void Main(string[] args)

   {

       Shape[] allShapes = new Shape[4];

       allShapes[0] = new Rectangle(1, 24, 10, 20);

       allShapes[1] = new Point(10, 25);

       allShapes[2] = new Point(1, 25);

       allShapes[3] = new Rectangle(1, 4, 10, 20);

       foreach (Shape s in allShapes)

       {

           s.Draw();

       }

   }

}

 

Типы перечислений

Типом перечисления называют тип, в котором описан набор пар, состоящих из символьного имени и числового значения. Тип перечисления относятся к типам значениям. Пример определения типа перечисления:

 

В примере объявляется тип перечисления  с именем Color, содержащий элементы списка перечислителя  Red, Green и Blue.

Объявление перечислени я

Объявление перечисления объявляет новый перечисляемый тип. Объявление перечисления начинается с зарезервированного слова enum и содержит определение имени, доступности, базового типа и членов перечисления.

Enum declarations

enum-declaration:
attributesopt enum-modifiersopt enum identifier enum-baseopt enum-body ;opt

объявление_перечисления:
атрибутынеобязательно модификаторы_перечислениянеобязательно enum идентификатор база_перечислениянеобязательно тело_перечисления ;необязательно

 

Каждый тип перечисления имеет соответствующий целый тип, называемый базовым типом типа перечисления. Этот базовый тип должен иметь возможность представлять все значения перечислителя, определенные в перечислении. Объявление перечисления может явно объявлять базовый тип byte, sbyte, short, ushort, int, uint, long или ulong. Тип char не может использоваться в качестве базового типа. Объявление перечисления, которое не содержит явное объявления базового типа, имеет базовый тип int.

Например:

enum Color: long
{
       Red,
       Green,
       Blue
}

Здесь объявляется перечисление с базовым типом long.








Модификаторы перечисления .

Объявление перечисления может, при необходимости, включать последовательность модификаторов перечисления:

new
public
protected
internal
private

 

Возникает ошибка времени компиляции, если один и тот же модификатор встречается несколько раз в объявлении перечисления. Модификаторы new, protected, private допустимы только если перечисление является вложенным.

Тело объявления перечисляемого типа определяет нуль или более членов перечисления, которые являются именованными константами перечисляемого типа. Два члена не могут иметь одинаковое имя. Каждый член перечисления имеет связанное с ним постоянное значение. Несколько членов перечисления могут совместно использовать одно и то же связанное значение.

Тип System.Enum.

Тип System.Enum — это абстрактный базовый класс всех перечисляемых типов. члены, унаследованные от System.Enum, доступны в любом перечисляемом типе.





Примеры работы с перечислениями.

namespace ConsoleApplication3

{

class Program

{

   public enum namemethod

   {

       method1,

       method2,

       method3

   }

   static void Main(string[] args)

   {

       Console.WriteLine(Enum.GetUnderlyingType(typeof(namemethod)));

   }

}

 }

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

using System;

namespace ConsoleApplication3

{

class Program

{

   public enum namemethod

   {

       method1,

       method2,

       method3

   }

   static void Main(string[] args)

   {

   namemethod temp = (namemethod)Enum.Parse(typeof(namemethod), Console.ReadLine());

       Console.WriteLine(temp.ToString("G"));

   }

}

 }

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

using System;

namespace ConsoleApplication3

{

class Program

{

   public enum namemethod

   {

       method1,

       method2,

       method3

   }

   static void Main(string[] args)

   {

       Console.WriteLine(Enum.Format(typeof(namemethod),Int32.Parse(Console.ReadLine()),"G"));

   }

}

 }

Пример

 enum en

{

   a=9,b=10,c=11

}

class Program

{

   static void Main(string[] args)

   {

       string s = Console.ReadLine();

       en myen = (en)Enum.Parse(typeof(en), s);

       Console.WriteLine(myen.ToString("G"));

   }

}

 

Пример

 

enum en

{

   a=9,b=10,c=11

}

class Program

{

   static void Main(string[] args)

   {

       string s = Console.ReadLine();

       en myen = (en)Enum.Parse(typeof(en), s);

       Console.WriteLine(myen.ToString("G"));

       string[] mystr =Enum.GetNames(typeof(en));

       Console.WriteLine(mystr[2]);

   }

}

Пример

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace ConsoleApplication1

{

enum VehicleDoors { Motorbike = 0, Sportscar = 2, Sedan = 4, Hatchback = 5 };

 

 

class Program

{

   static void Main(string[] args)

   {

       string s=Enum.GetName(typeof(VehicleDoors), 4);

           

       Console.WriteLine(s);

 

       VehicleDoors myVeh = VehicleDoors.Sportscar;

       VehicleDoors yourVeh = VehicleDoors.Motorbike;

 

                

       Console.WriteLine(myVeh.CompareTo(yourVeh));

          

       VehicleDoors hh = new VehicleDoors();

          Console.WriteLine(hh);

           

   }

}

}

 

Интерфейсы.

Интерфейс – ссылочный тип. Интерфейс представляет способ задать имя набору сигнатур методов (можно определять методы, события, индексаторы  и свойства). Интерфейс не содержит реализации методов. Класс наследует интерфейс через указание имени интерфейса и должен явно содержать реализации методов интерфейса.

Объявление интерфейса является объявлением типа, где объявляется новый тип интерфейса.

Interface declarations

interface-declaration:
attributesopt interface-modifiersopt partialopt interface identifier type-parameter-listopt
            interface-baseopt   type-parameter-constraints-clausesopt interface-body ;opt

 

объявление_интерфейса:
атрибутынеобязательно модификаторы_интерфейсанеобязательно partialнеобязательно interface             идентификатор список_параметров_типа_вариантанеобязательно база_интерфейсанеобязательно
            предложения_ограничений_параметров_типанеобязательно тело_интерфейса ;необязательно

 

модификаторы_интерфейса:
new
public
protected
internal
private

Модификатор new (protected , private ) разрешен только в том случае, если интерфейс определяется внутри класса. Модификатор new указывает, что в интерфейсе скрыт унаследованный член с таким же именем.

В теле_интерфейса определяются члены этого интерфейса.

тело_интерфейса:
{ объявления_элементов_интерфейсанеобязательно }

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

объявление_членов_интерфейса:
объявление_метода_интерфейса
объявление_свойства_интерфейса
объявление_события_интерфейса
объявление_индексатора_интерфейса

Например:

public delegate void StringListEvent(IStringList sender);

public interface IStringList
{
       void Add(string s);

       int Count { get; }

       event StringListEvent Changed;

       string this[int index] { get; set; }
}

Объявляется интерфейс, который содержит по одному из всех допустимых видов членов: метод, свойство, событие и индекс.

К объявлениям членов интерфейса применяются следующие правила:

Имя метода должно отличаться от имен всех свойств и событий, объявленных в том же интерфейсе. Помимо этого подпись метода должна отличаться от подписей всех других методов, объявленных в том же интерфейсе, а два метода, объявленные в одном интерфейсе, не могут иметь подписи, отличающиеся только модификаторами ref и out.

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

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

 

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

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

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

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

 

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

Пример Форматирование даты в зависимости от языка и региональных параметров

 

 

provider

Тип: System.IFormatProvider
Объект IFormatProvider, предоставляющий сведения о форматировании, связанные с определенным языком и региональными параметрами.

provider переменная типа System.IFormatProvider. Нельзя создать экземпляр  интерфейса IFormatProvider с помощью оператора new.

 

 

У интерфейсов не бывает конструкторов. Можно создать переменную типа интерфейса и присвоить ей, ссылку на экземпляр класса, производного от интерфейса.

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace ConsoleApplication10

{

class Program

{

   static void Main(string[] args)

   {

       DateTime[] dt = new DateTime[3] { new DateTime(2012, 3, 5), new DateTime(2012, 5, 4), new DateTime(2012, 3, 7) };

 

       String st = String.Format(new System.Globalization.CultureInfo("en-US"), "{0}", dt[0]);

       Console.WriteLine(st);

   }

}

}

В перегруженном методе String.Format мы можем создать экземпляр класса System.Globalization.CultureInfo, потому что класс System.Globalization.CultureInfo наследует от интерфейса IFormatProvider

Члены интерфейса неявно имеют уровень доступа «public». Включение модификаторов в объявления членов интерфейса приведет к возникновению ошибки компилирования. Следующий код приведет к ошибке:

interface IPoint

{

// Property signatures:

public int x {get; set;}

int y {get; set;}

}

Ошибка    1     Модификатор " public" недопустим для этого элемента 

Реализация интерфейсов.



















Пример.

using System;

namespace ConsoleApplication10

{

interface IPoint

{

   // Property signatures:

   int x { get; set; }

   int y { get; set; }

}

class Point : IPoint

{

   // Fields:

   private int _x;

   private int _y;

   // Constructor:

   public Point(int x, int y)

   {

       _x = x;

       _y = y;

   }

   // Property implementation:

   public int x

   {

       get { return _x; }

       set { _x = value; }

   }

   public int y

   {

       get { return _y; }

       set { _y = value; }

   }

}

class Program

{

   static void Main()

   {

       Point p = new Point(2, 3);

       Console.Write("My Point: ");

       Console.WriteLine("x={0}, y={1}", p.x, p.y);

   }

}

}

Код приведет к ошибке:

IPoint ip = new IPoint();

 

Ошибки не будет:

using System;

namespace ConsoleApplication10

{

interface IPoint

{

   // Property signatures:

   int x { get; set; }

   int y { get; set; }

}

class Point : IPoint

{

   // Fields:

   private int _x;

   private int _y;

   // Constructor:

   public Point(int x, int y)

   {

       _x = x;

       _y = y;

   }

   // Property implementation:

   public int x

   {

       get { return _x; }

       set { _x = value; }

   }

   public int y

   {

       get { return _y; }

       set { _y = value; }

   }

}

class Program

{

   static void Main()

   {

       Point p = new Point(2, 3);

       IPoint ip = p;

       Console.Write("My Point: ");

       Console.WriteLine("x={0}, y={1}", ip.x, ip.y);

   }

}

}

В приведенном выше примере свойства в классе имеют модификатор доступа public

Если модификатор public отсутствует, то надо написать следующий код:

using System;

namespace ConsoleApplication10

{

interface IPoint

{

   // Property signatures:

   int x { get; set; }

   int y { get; set; }

}

class Point : IPoint

{

   // Fields:

   private int _x;

   private int _y;

   // Constructor:

   public Point(int x, int y)

   {

       _x = x;

       _y = y;

   }

   // Property implementation:

   int IPoint.x

   {

       get { return _x; }

       set { _x = value; }

   }

   int IPoint.y

   {

       get { return _y; }

       set { _y = value; }

   }

}

class Program

{

   static void Main()

   {

       Point p = new Point(2, 3);

       IPoint ip = p;

       Console.Write("My Point: ");

       Console.WriteLine("x={0}, y={1}", ip.x, ip.y);

   }

}

}

 Это использование явной реализации члена интерфейса. Включение в явную реализацию члена интерфейса модификаторов доступа, а также модификаторов abstract, virtual, override или static, приведет к ошибке при компилировании.

 

К явным реализациям члена интерфейса нельзя получить доступ через экземпляры классов или структур.

Явные реализации члена интерфейса позволяют разрешить неоднозначности членов интерфейса с одинаковой подписью.

 

using System;

namespace ConsoleApplication10

{

interface IPoint

{

   // Property signatures:

   int x { get; set; }

   int y { get; set; }

}

class Point : IPoint

{

   // Fields:

   private int _x;

   private int _y;

   // Constructor:

   public Point(int x, int y)

   {

       _x = x;

       _y = y;

   }

   // Property implementation:

   public int x

   {

       get { return _x; }

       set { _x = value; }

   }

   int IPoint.y

   {

       get { return _y; }

       set { _y = value; }

   }

}

class Program

{

   static void Main()

   {

       Point p = new Point(2, 3);

       IPoint ip = p;

       Console.Write("My Point: ");

       Console.WriteLine("x={0}, y={1}",p.x, ip.y);

   }

}

}



Делегаты

Объявление делегата определяет класс, производный от класса System.Delegate.

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

Объявление делегата — это объявление типа, которое объявляет новый тип делегата.

Delegate declarations

delegate-declaration:
attributesopt delegate-modifiersopt delegate return-type identifier type-parameter-listopt
            ( formal-parameter-listopt ) type-parameter-constraints-clausesopt ;

 

объявление_делегата:
атрибутынеобязательно модификаторы_делегатанеобязательно delegate возвращаемый_тип
            идентификатор список_параметров_типа_вариантанеобязательно
            ( список_формальных_параметровнеобязательно ) предложения_ограничений_параметров_типанеобязательно ;

модификаторы делегата:
new
public
protected
internal
private

Возникает ошибка времени компиляции, если один и тот же модификатор встречается несколько раз в объявлении делегата.

Модификатор new допускается только для делегатов, объявленных в другом типе; в этом случае модификатор указывает, что такой делегат скрывает унаследованный член с тем же именем.

Модификаторы public, protected, internal и private управляют доступностью типа делегата. В зависимости от контекста, в котором встречается объявление делегата, некоторые из этих модификаторов запрещены.

Именем типа делегата является идентификатор.

Необязательный список формальных параметров определяет параметры делегата, а тип возвращаемого значения указывает тип возвращаемого значения делегата.

Два разных типа делегатов с одинаковыми списками параметров и типом возвращаемого значения считаются разными типами делегатов.

Например

delegate int D1(int i, double d);

delegate int D2(int c, double d);

Объявление_делегата — это единственный способ объявить тип делегата. Тип делегата является типом класса, производного от System.Delegate. Типы делегатов неявно sealed, поэтому никакие производные типы от типа делегата не допускаются. Недопустимо также производить тип класса не делегата от System.Delegate. Обратите внимание, что System.Delegate сам не является типом делегата; это тип класса, от которого производятся все типы делегатов.

 

Набор методов, инкапсулированных экземпляром делегата, называется списком вызова. Если экземпляр делегата создается из единственного метода, он инкапсулирует этот метод, и его список вызова содержит только одну запись. Однако, когда объединяются два непустых экземпляра делегата, их списки вызова связываются — в очередности: левый операнд, а затем правый операнд — для формирования нового списка вызова, содержащего две или более записей.

Делегаты объединяются с помощью бинарных операторов + и +=. Делегат можно удалить из объединения делегатов с помощью бинарных операторов - и -=. Делегаты можно проверять на равенство.

В следующем примере показано создание нескольких экземпляров делегата и их соответствующие списки вызова:

delegate void D(int x);

class C
{
          public static void M1(int i) {...}

public static void M2(int i) {...}

}

class Test
{
          static void Main() {
                          D cd1 = new D(C.M1);                    // M1
                          D cd2 = new D(C.M2);                    // M2
                          D cd3 = cd1 + cd2;                         // M1 + M2
                          D cd4 = cd3 + cd1;                         // M1 + M2 + M1
                          D cd5 = cd4 + cd3;                         // M1 + M2 + M1 + M1 + M2
          }

}

При создании экземпляров cd1 и cd2 каждый из них инкапсулирует один метод. При создании экземпляра cd3 его список вызова содержит два метода, M1 и M2, в этом порядке. Список вызова экземпляра cd4 содержит методы M1, M2 и M1, в этом порядке. Наконец, список вызова экземпляра cd5 содержит методы M1, M2, M1, M1 и M2, в этом порядке.





















Пример

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace ConsoleApplication1

{

delegate void D(int x);

class C

{

  public static void M1(int i) {

        Console.WriteLine("C.M1: " + i);

  }

  public static void M2(int i) {

        Console.WriteLine("C.M2: " + i);

  }

  public void M3(int i) {

    Console.WriteLine("M3: " + i);

  }

}

class Test

{

static void Main()

{

   D cd1 = new D(C.M1);

   cd1(-1);                   // вызов M1

   D cd2 = new D(C.M2);

   cd2(-2);                   // вызов M2

   D cd3 = cd1 + cd2;

   cd3(10);                   // вызов M1, а затем M2

   cd3 += cd1;

   cd3(20);                   // вызов M1, M2, а затем M1

   C c = new C();

   D cd4 = new D(c.M3);

   cd3 += cd4;

   cd3(30);                   // вызов M1, M2, M1, а затем M3

   cd3 -= cd1;          // удаление последнего M1

   cd3(40);                   // вызов M1, M2, а затем M3

   cd3 -= cd4;

   cd3(50);                   // вызов M1, а затем M2

   cd3 -= cd2;

   cd3(60);                   // вызов M1

   cd3 -= cd2;          //

   cd3(60);                   // вызов M1

   cd3 -= cd1;          // список вызова пуст, поэтому cd3 имеет значение null

   //     cd3(70);       // возникло исключение System.NullReferenceException

   cd3 -= cd1;          //

}

}

 

 

}

 

Выражения анонимных функций

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

Существует две синтаксические разновидности анонимных функций, а именно: лямбда_выражения и выражения_анонимных_методов. Практически для любых целей лямбда_выражения более конкретны и точны, чем выражения_анонимных_методов, которые остаются в языке в целях обратной совместимости.

лямбда_выражение:
подпись_анонимной_функции => тело_анонимной_функции

выражение_анонимного_метода:
delegate подпись_явной_анонимной_функциинеобязательно блок

 

Оператор => имеет такой же приоритет, как и присваивание (=) и обладает правой ассоциативностью.

Тип параметров анонимной функции в виде лямбда_выражения может задаваться явно или неявно. В списке явно типизированных параметров тип каждого параметра указывается явно. В списке неявно типизированных параметров типы параметров выводятся из контекста, в котором находится анонимная функция, в частности, когда анонимная функция преобразуется в совместимый тип делегата или дерева выражения, типы параметров предоставляются этим типом.

В анонимной функции с одним параметром с неявной типизацией в списке параметров можно опустить скобки. Другими словами запись анонимной функции вида

( param ) => выражение

можно сократить до:

param => выражение

Пример

namespace ConsoleApplication1

{

delegate int del(int i);

class Program

{

   static void Main(string[] args)

   {

 

       del objdel = x => x + 1; // Implicitly typed, expression body

       Console.WriteLine(objdel(5));

   }

}

}

Список параметров анонимной функции в виде выражения_анонимного_метода является необязательным. Если они заданы, то параметры должны быть явно типизированы Если они не задаются, то анонимную функцию можно преобразовать в делегат с любым списком параметров, не содержащих параметров out.

Ниже приведены некоторые примеры анонимных функций.

x => x + 1                          // Implicitly typed, expression body

x => { return x + 1; }              // Implicitly typed, statement body

(int x) => x + 1                    // Explicitly typed, expression body

(int x) => { return x + 1; } // Explicitly typed, statement body

(x, y) => x * y                     // Multiple parameters

() => Console.WriteLine()           // No parameters

delegate (int x) { return x + 1; }        // Anonymous method expression

delegate { return 1 + 1; }                // Parameter list omitted

Поведение лямбда_выражений и выражений_анонимных_методов совпадает за исключением следующих моментов:

· выражения_анонимных_методов позволяют опускать список параметров целиком, обеспечивая возможность преобразования в типы делегатов с любым списком параметров значения.

· лямбда_выражения позволяют опускать и выводить типы параметров, тогда как в выражениях_анонимных_методов типы параметров должны быть указаны явно.

· Тело лямбда_выражения может быть выражением или блоком оператора, тогда как тело выражения_анонимного_метода должно быть блоком оператора.

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

 

namespace ConsoleApplication1

{

delegate int del(int i);

class Program

{

   static void Main(string[] args)

   {

 

       del objdel = x => x + 1;           // Implicitly typed, expression body

       Console.WriteLine(objdel(5));

 

       objdel = x => { return x + 1; };     // Implicitly typed, statement body

       objdel = (int x) => x + 1;           // Explicitly typed, expression body

       objdel = (int x) => { return x + 1; }; // Explicitly typed, statement body

       objdel = delegate(int x) { return x + 1; }; // Anonymous method expression

       objdel = delegate { return 1 + 1; };       // Parameter list omitted

 

   }

}

}

 

 

Лямбда-выражение

Лямбда-выражение — это анонимная функция, которая содержит выражения и операторы и может использоваться

Для создания делегатов

Для создания типов дерева выражений.

Пример

 

namespace ConsoleApplication1

{

delegate int del(int i);

class Program

{

   static void Main(string[] args)

   {

       del objdel = x => x + 1;

       Console.WriteLine(objdel(5));

 

       Expression<del> myET = x => x * x;

       del delobj = myET.Compile();

       int i = delobj(5);

       Console.WriteLine(i);

   }

}

}

Или создание типа дерева выражений

 

namespace ConsoleApplication1

{

delegate int del(int i);

class Program

{

   static void Main(string[] args)

   {

       Expression<del> myET = x => x * x;

       int i = myET.Compile()(6);

       Console.WriteLine(i);

   }

}

}

 

 

Action - делегат

Пространство имен: System

Инкапсулирует метод, который не принимает ни одного параметра и не возвращает значений.

 

public delegate void Action()

 

Этот делегат можно использовать для передачи метода в качестве параметра без явного объявления пользовательского делегата.

 

Пример

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Linq.Expressions;

using System.Text;

 

namespace ConsoleApplication1

{

   

class Program

{

   static void Main(string[] args)

   {

 

       Expression<Action> printExpr = () => Console.WriteLine("hhh");

       // Compiling and invoking the expression tree.

       printExpr.Compile()();

 

   }

}

 

}

 

 

Func<TResult> - делегат

Инкапсулирует метод без параметров и возвращает значение типа, указанного в параметре TResult.

 

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

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Linq.Expressions;

using System.Text;

 

namespace ConsoleApplication1

{

class Program

{

   static bool met() { return true; }

   static void Main(string[] args)

   {

 

       Expression<Func<bool>> printExpr = () => met();

       // Compiling and invoking the expression tree.

       Console.WriteLine(printExpr.Compile()());

 

   }

}

}

 

Создание типа дерева выражений:

 

Можно создать дерево выражений на основе анонимного лямбда-выражения с использованием компилятора C# (как в примере выше) или создать деревья выражений вручную с использованием пространства имен System.Linq.Expressions.

Абстрактный класс Expression обеспечивает корень иерархии класса, используемый для моделирования деревьев выражений.

Классы в этом пространстве имен, которые наследуются от Expression, например MemberExpression и ParameterExpression, используются для представления узлов в дереве выражения.

 

Пример создания выражения

 

BinaryExpression – класс представляет выражение, содержащее бинарный оператор.

В следующем примере создается объект BinaryExpression, представляющий вычитание одного числа из другого.

 

class Program

{

   static void Main(string[] args)

   {

 

       System.Linq.Expressions.BinaryExpression binaryExpression =

System.Linq.Expressions.Expression.MakeBinary(

   System.Linq.Expressions.ExpressionType.Subtract,

   System.Linq.Expressions.Expression.Constant(53),

   System.Linq.Expressions.Expression.Constant(14));

 

       Console.WriteLine(binaryExpression.ToString());

         

 

   }

}

 

Выполнение *дерева выражений

 

using System.Linq;

using System.Linq.Expressions;

using System.Text;

 

namespace ConsoleApplication1

{

delegate int del();

class Program

{

   static void Main(string[] args)

   {

 

       BinaryExpression binaryExpression = Expression.MakeBinary(ExpressionType.Subtract, Expression.Constant(13), Expression.Constant(4));

 

       Expression<del> myET = Expression.Lambda<del>(binaryExpression);

       del delobj = myET.Compile();

        int i = delobj();

       Console.WriteLine(i);

   }

}

}

 

Лямбда операторов напоминает выражение-лямбду, за исключением того, что оператор (или операторы) заключается в фигурные скобки:

 

Пример Код не содержит ошибок

 

namespace ConsoleApplication1

{

class Program

{

   static void Main(string[] args)

   {

       Action printTwoLines = () =>

       {

           Console.WriteLine("Print arg:");

           Console.WriteLine("nnnn");

       };

       printTwoLines();

   }

}

}

Этот код содержит ошибку

 

Expression<Action> printTwoLines = () => { Console.WriteLine("Print arg:"); };

 

Ошибка     1     Лямбда-выражение с телом оператора не может быть преобразовано в дерево выражения   

 

Компилятор  C# может создавать деревья выражений только из лямбд выражений (или однострочных лямбд). Эот компилятор не может выполнить синтаксический анализ лямбд операторов (или многострочных лямбд).

 

BlockExpression - класс

 

Представляет блок, содержащий последовательность выражений, в котором могут быть определены переменные.

 

Пример (MSDN)

namespace ConsoleApplication1

{

class Program

{

   static int CSharpFact(int value)

   {

       int result = 1;

       while (value > 1)

       {

           result *= value--;

       }

       return result;

   }

   static void Main(string[] args)

   {

       Console.WriteLine(CSharpFact(5));

   }

}

class Program

{

   static int CSharpFact(int value)

   {

       int result = 1;

       while (value > 1)

       {

           result *= value--;

       }

       return result;

   }

   static void Main(string[] args)

   {

       Expression<Func<int,int>> printExpr = (v) => CSharpFact(v);

       Console.WriteLine(printExpr.Compile()(5));

       Console.WriteLine(CSharpFact(5));

   }

}

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

namespace ConsoleApplication1

{

class Program

{

   static void Main(string[] args)

   {

       ParameterExpression value = Expression.Parameter(typeof(int), "value");

 

       // Creating an expression to hold a local variable.

       ParameterExpression result = Expression.Parameter(typeof(int), "result");

 

       // Creating a label to jump to from a loop.

       LabelTarget label = Expression.Label(typeof(int));

       // Creating a method body.

       BlockExpression block = Expression.Block(

           // Adding a local variable.

           new[] { result },

           // Assigning a constant to a local variable: result = 1

           Expression.Assign(result, Expression.Constant(1)),

           // Adding a loop.

               Expression.Loop(

           // Adding a conditional block into the loop.

                  Expression.IfThenElse(

           // Condition: value > 1

                      Expression.GreaterThan(value, Expression.Constant(1)),

           // If true: result *= value --

                      Expression.MultiplyAssign(result,

                          Expression.PostDecrementAssign(value)),

           // If false, exit the loop and go to the label.

                      Expression.Break(label, result)

                  ),

           // Label to jump to.

              label

           )

       );

 

       // Compile and execute an expression tree.

       int factorial = Expression.Lambda<Func<int, int>>(block, value).Compile()(5);

 

       Console.WriteLine(factorial);

    }

}

}

LINQ (Language-Integrated Query)

 

Все операции запроса LINQ состоят из трех различных действий.

1. Получение источника данных.

2. Создание запроса.

3. Выполнение запроса.

 

Пример (MSDN)

В примере в качестве источника данных для удобства используется массив целых чисел;

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Linq.Expressions;

using System.Text;

 

namespace ConsoleApplication1

{

class Program

{

      

   static void Main(string[] args)

   {

       // The Three Parts of a LINQ Query:

       // 1. Data source.

       int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };

 

       // 2. Query creation.

       // numQuery is an IEnumerable<int>

       var numQuery =

           from num in numbers

             where (num % 2) == 0

           select num;

 

       // 3. Query execution.

       foreach (int num in numQuery)

       {

           Console.Write("{0,1} ", num);

       }

 

   }

}

 

}

 

 

Выражения запросов составляются в соответствии с декларативным синтаксисом запроса

 

Основные операции запросов LINQ (C#)

 

В запросе LINQ первым идет предложение from для указания источника данных

Наиболее распространенной операцией запроса является применение фильтра в виде логического выражения. Фильтр приводит к возвращению запросом только тех элементов, для которых выражение является истинным. Результат создается с помощью предложения where.

Предложение orderby сортирует элементы возвращаемой последовательности в зависимости от компаратора по умолчанию для сортируемого типа. Например,

 

namespace ConsoleApplication1

{

class Program

{

      

   static void Main(string[] args)

   {

       // The Three Parts of a LINQ Query:

       // 1. Data source.

       int[] numbers = new int[7] { 0,4, 22, 13, 34, 5, 6 };

 

       // 2. Query creation.

       // numQuery is an IEnumerable<int>

       var numQuery =

           from num in numbers

           orderby num ascending

           select num;

 

       // 3. Query execution.

       foreach (int num in numQuery)

       {

           Console.Write("{0,1} ", num);

       }

 

   }

}

 

}

 

orderby num descending

 

 

 

Предложение group позволяет группировать результаты на основе указанного ключа.

 

В выражении запроса предложение select задает тип значений, получаемых при выполнении запроса.

 

Подробнее в http://msdn.microsoft.com/ru-ru/library/bb310804(v=vs.100).aspx

 

 

Использование лямбда-выражений в запросах

 

 

Во время компиляции выражения запроса преобразуются в то, что понятно CLR — вызовы методов. Эти методы называются стандартными операторами запросов и имеют такие имена, как Where, Select, GroupBy

В следующем примере показано простое выражение запроса и семантически эквивалентный ему запрос, написанный как запрос на основе метода.

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Linq.Expressions;

using System.Text;

 

namespace ConsoleApplication1

{

class Program

{

      

   static void Main(string[] args)

   {

       int[] numbers = { 5, 10, 8, 3, 6, 12 };

 

       //Query syntax:

       IEnumerable<int> numQuery1 =

           from num in numbers

           where num % 2 == 0

           orderby num

           select num;

 

       //Method syntax:

       IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);

 

       foreach (int i in numQuery1)

       {

           Console.Write(i + " ");

       }

       Console.WriteLine(System.Environment.NewLine);

       foreach (int i in numQuery2)

       {

           Console.Write(i + " ");

       }

 

       // Keep the console open in debug mode.

       Console.WriteLine(System.Environment.NewLine);

       Console.WriteLine("Press any key to exit");

       Console.ReadKey();

   }

}

 

}

 

Условное выражение (num % 2 == 0) передается в качестве встроенного аргумента методу Where: Where(num => num % 2 == 0) и является лямбда-выражением.

 

 

int[] numbers = { 3, 4, 5};

 

Console.WriteLine( numbers.Average());

 

Пример

В следующем примере показано простое выражение запроса и семантически эквивалентный ему запрос, написанный как запрос на основе метода.

 

namespace ConsoleApplication1

{

 

public class Student

{

   public string First { get; set; }

   public string Last { get; set; }

   public int ID { get; set; }

   public int[] Scores;

 

   public override string ToString()

   {

       return First + "\t " + Last + "\t" + ID;

   }

}

 

public class Program

{

 

   static void Main(string[] args)

   {

          

        Student[] students = new Student[4]{new Student {First="Sveta", Last="Omelchenko", ID=111, Scores= new int[] {97, 92, 81, 60}},

        new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new int[] {5, 4, 3, 4}},

        new Student {First="Sven", Last="Mortensen", ID=113, Scores= new int[] {88, 94, 65, 91}},

        new Student {First="Cesar", Last="Garcia ", ID=114, Scores= new int[] {97, 89, 85, 82}}};

 

        foreach (Student s in students)

        {

            Console.WriteLine(s.ToString());

        }

 

        //// Produce a filtered sequence of unmodified Students.

        //IEnumerable<Student> studentQuery1 =

        // from student in students

        // where student.ID > 111

        // select student;

 

       IEnumerable<Student> numQuery1 = students.Where(stud => stud.ID > 111);

            

       Console.WriteLine();

        Console.WriteLine("Query1: select range_variable");

        foreach (Student s in numQuery1)

        {

            Console.WriteLine(s.ToString());

        }

 

        // Produce a filtered sequence of elements that contain

        // only one property of each Student.

        //IEnumerable<String> studentQuery2 =

        // from student in students

        // where student.ID > 111

        // select student.Last;

 

 

        IEnumerable<String> numQuery2 = students.Where(stud => stud.ID > 111).Select(stud => stud.Last);

        Console.WriteLine();

       Console.WriteLine("studentQuery2: select range_variable.Property");

       foreach (string s in numQuery2)

        {

            Console.WriteLine(s);

        }

 

        // Produce a filtered sequence of ints from

        // the internal array inside each Student.

        //IEnumerable<int> studentQuery4 =

        // from student in students

        // where student.ID > 111

        // select student.Scores[0];

 

       IEnumerable<int> numQuery4 = students.Where(stud => stud.ID > 111).Select(stud => stud.Scores[0]);

       Console.WriteLine("\r\n studentQuery4: select range_variable[index]");

       foreach (int i in numQuery4)

        {

            Console.WriteLine("First score = {0}", i);

        }

 

        // Produce a filtered sequence of doubles

        // that are the result of an expression.

        //IEnumerable<double> studentQuery5 =

        // from student in students

        // where student.ID > 111

        // select student.Scores[0] * 1.1;

 

        IEnumerable<double> numQuery5 = students.Where(stud => stud.ID > 111).Select(stud => stud.Scores[0]*1.1);

       Console.WriteLine("\r\n studentQuery5: select expression");

        foreach (double d in numQuery5)

        {

            Console.WriteLine("Adjusted first score = {0}", d);

        }

 

        //// Produce a filtered sequence of doubles that are

        //// the result of a method call.

        //IEnumerable<double> studentQuery6 =

        // from student in students

        // where student.ID > 111

        // select student.Scores.Average();

            

       IEnumerable<double> numQuery6 = students.Where(stud => stud.ID > 111).Select(stud => stud.Scores.Average());

        Console.WriteLine("\r\n studentQuery6: select expression2");

        foreach (double d in numQuery6)

        {

            Console.WriteLine("Average = {0}", d);

        }

 

        // Produce a filtered sequence of anonymous types

        // that contain only two properties from each Student.

        //var studentQuery7 =

        // from student in students

        // where student.ID > 111

        // select new { student.First, student.Last };

 

        IEnumerable<string> numQuery7 = students.Where(stud => stud.ID > 111).Select(stud => stud.First + stud.Last);

        Console.WriteLine("\r\n studentQuery7: select new anonymous type");

        foreach (string item in numQuery7)

        {

            Console.WriteLine("{0}", item);

        }

       

   }

}

  

}


 




Универсальные шаблоны

Пример

class MyClass

{

int a;

int b;

public MyClass(int a, int b)

{

   this.a = a;

   this.b = b;

}

public override string ToString()

{

   return String.Format("{0} {1}", a, b);

}

}

class Program

{

static void Main(string[] args)

{

   MyClass myobject = new MyClass(3, 6);

   Console.WriteLine(myobject);

}

}

 

using System;

class MyClass<T>

{

T a;

T b;

public MyClass(T a, T b)

{

   this.a = a;

   this.b = b;

}

public override string ToString()

{

   return String.Format("{0} {1}", a, b);

}

}

class Program

{

static void Main(string[] args)

{

   MyClass<string> myobject = new MyClass<string>("3", "6");

   Console.WriteLine(myobject);

}

}

using System;

class MyClass<T1,T2>

{

T1 a;

T2 b;

public MyClass(T1 a, T2 b)

{

   this.a = a;

   this.b = b;

}

public override string ToString()

{

   return String.Format("{0} {1}", a, b);

}

}

class Program

{

static void Main(string[] args)

{

   MyClass<string,int> myobject = new MyClass<string,int>("3", 6);

   Console.WriteLine(myobject);

}

}

 

Использование неуниверсальных классов коллекции (msdn и спецификация C#)

Пример

namespace ConsoleApplication1

{

class Program

{

   static void Main(string[] args)

   {

       System.Collections.ArrayList list1 = new System.Collections.ArrayList();

       list1.Add(3);

       list1.Add(105);

 

       System.Collections.ArrayList list2 = new System.Collections.ArrayList();

       list2.Add("It is raining in Redmond.");

       list2.Add("It is snowing in the mountains.");

 

 

   }

}

}

 

Любые ссылки или типы значения, добавляемые к классу коллекции ArrayList, неявно приводятся к Object. Если в качестве элементов выступают типы значений, то для них необходимо выполнить упаковку-преобразование при добавлении к списку и распаковку-преобразование при извлечении из списка. Операции приведения, упаковки-преобразования и распаковки-преобразования снижают производительность. Влияние операций упаковки-преобразования и распаковки-преобразования в сценариях обработки больших коллекций может быть весьма значительным.

Еще одно ограничение состоит в отсутствии проверки типа во время компиляции. Поскольку класс коллекции ArrayList приводит все к типу Object, невозможно предотвратить выполнение клиентским кодом во время компиляции действий, подобных следующим:

       System.Collections.ArrayList list = new System.Collections.ArrayList();

       // Add an integer to the list.

       list.Add(3);

       // Add a string to the list. This will compile, but may cause an error later.

       list.Add("It is raining in Redmond.");

 

       int t = 0;

       // This causes an InvalidCastException to be returned. Ошибка !!

       foreach (int x in list)

       {

           t += x;

       }

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

В универсальном шаблоне коллекции List<T> в пространстве имен N:System.Collections.Generic та же операция добавления элементов в коллекцию выглядит следующим образом:

namespace ConsoleApplication1

{

class Program

{

   static void Main(string[] args)

   {

       // The .NET Framework 2.0 way to create a list

       List<int> list1 = new List<int>();

 

       // No boxing, no casting:

       list1.Add(3);

 

       // Compile-time error:

       // list1.Add("It is raining in Redmond.");

 

   }

}

}

Единственным различием в клиентском коде между List<T> и ArrayList является аргумент типа <T> в разделе объявления и создания экземпляра. Благодаря этому небольшому усложнению кода можно создать список, который будет не только безопаснее, чем ArrayList, но и существенно быстрее, особенно если элементами списка являются типы значений.

Первичное ограничение

Ограничение типа_класса должно удовлетворять следующим правилам.

· Типом должен быть тип класса.

· Тип не должен быть sealed.

· Тип не должен быть одним из следующих типов: System.Array, System.Delegate, System.Enum или System.ValueType.

· Тип не должен быть object. Так как все типы являются производными от object, отсутствие данного ограничения не оказывает никакого влияния.

· Максимум одно ограничение для данного параметра типа может являться типом класса.

Одно первичное ограничение, одно или несколько вторичных ограничений и ограничение конструктора , new().

Пример

 

public class Employee { }

public struct EmployeeStruct { }

 

public class Pair<TFirst, TSecond>

   where TSecond : class

{

   public TFirst First;

   public TSecond Second;

}

 

class Program

{

   static void Main(string[] args)

   {

       Pair<int, Employee> obj = new Pair<int, Employee>();

       obj.First = 5;

       obj.Second = new Employee();

   }

}

 

namespace ConsoleApplication1

{

public class Employee { }

public struct EmployeeStruct { }

  

public class Pair<TFirst, TSecond>

   where TSecond : Employee

{

   public TFirst First;

   public TSecond Second;

}

  

class Program

{

   static void Main(string[] args)

   {

   }

}

}

Следующий пример содержит ошибку в строке кода where TSecond : class, Employee

невозможно одновременно задать класс ограничения и ограничения "class" или "struct"   

 

namespace ConsoleApplication1

{

public class Employee { }

public struct EmployeeStruct { }

  

public class Pair<TFirst, TSecond>

   where TSecond : class, Employee

{

   public TFirst First;

   public TSecond Second;

}

  

class Program

{

   static void Main(string[] args)

   {

    }

}

}

 

Следующий пример не содержит ошибок

namespace ConsoleApplication1

{

public class Employee { }

public struct EmployeeStruct { }

  

public class Pair<TFirst, TSecond>

   where TSecond : class

   where TFirst : struct

 

{

   public TFirst First;

   public TSecond Second;

}

  

class Program

{

   static void Main(string[] args)

   {

       Pair<int, string> pair = new Pair<int, string> { First = 1, Second = "two" };

   }

}

}

 

Вторичное ограничение может быть параметром_типа или типом_интерфейса

Тип, заданный в качестве ограничения тип_интерфейса, должен удовлетворять следующим правилам:

· Он должен быть типом интерфейса.

Тип не должен быть указан более одного раза в данном предложении where. (с одним и тем же именем)

Тип, заданный в качестве ограничения параметр_типа, должен удовлетворять следующим правилам.

· Он должен быть параметром типа.

· Тип не должен быть указан более одного раза в данном предложении where.

Пример Тип, заданный в качестве ограничения параметр_типа

public class SampleClass<T, U, V> where T : V { }

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

Не допускается наличие циклов в диаграмме зависимости параметров типа

 

Следующий пример содержит ошибку, так как он вызывает цикличность в графе зависимостей параметров типа.

class Circular<S,T>
where S: T
where T: S              // Error, circularity in dependency graph
{
...
}

 

Пример одно или несколько вторичных ограничений. Тип, заданный в качестве ограничения тип_интерфейса

namespace ConsoleApplication1

{

interface IPrintable

{

   void Print();

}

interface IComparable<TSecond>

{

   int CompareTo(TSecond value);

}

class Pair<TSecond>

   where TSecond : class, IPrintable, IComparable<TSecond>

{

}

 

class Program

{

   static void Main(string[] args)

   {

 

   }

}

}

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

Следующий пример содержит ошибку

Несовместимость по доступности: тип ограничения "ConsoleApplication1.IComparable<TSecond>" менее доступен, чем "ConsoleApplication1.Pair<TSecond>"      

namespace ConsoleApplication1

{

interface IPrintable

{

   void Print();

}

interface IComparable<TSecond>

{

   int CompareTo(TSecond value);

}

public class Pair<TSecond>

   where TSecond : class, IPrintable, IComparable<TSecond>

{

}

 

class Program

{

   static void Main(string[] args)

   {

 

   }

}

}

 

Пример

 

using System;

 

interface IComparable<TSecond>

{

int CompareTo(TSecond value);

}

class Pair<TSecond> where TSecond : class, IComparable<TSecond>

{

TSecond M;

public Pair(TSecond M)

{

   this.M = M;

}

public string Comp(TSecond M1)

{

   string s = "";

   if (M.CompareTo(M1) > 0) {s = "старше"; }

   if (M.CompareTo(M1) < 0) { s = "младше"; }

   return s;

}

}

class Person : IComparable<Person>

{

string name;

int age;

public Person(string s, int i)

{

   name = s;

   age = i;

}

public int CompareTo(Person p)

{

   return age - p.age;

}

 }

class Program

{

static void Main(string[] args)

{

   Pair<Person> g1 = new Pair<Person>(new Person("ggg1",18));

   Console.WriteLine(g1.Comp(new Person("ggg1", 22)));

}

}

 

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






Члены сформированных типов

Не унаследованные члены сформированного типа получаются путем замещения для каждого параметра_типа в объявлении члена соответствующего аргумента_типа сформированного типа. Процесс замещения основан на семантическом значении объявлений типа и не является простым текстовым замещением.

Например, в объявлении универсального класса

class Gen<T,U>
{
public T[,] a;

public void G(int i, T t, Gen<U,T> gt) {...}

public U Prop { get {...} set {...} }

public int H(double d) {...}
}

сформированный тип Gen<int[],IComparable<string>> имеет следующие члены

public int[,][] a;

public void G(int i, int[] t, Gen<IComparable<string>,int[]> gt) {...}

public IComparable<string> Prop { get {...} set {...} }

public int H(double d) {...}

Пример

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace ConsoleApplication1

{

class Gen<T, U>

{

   public T[,] a;

 

}

 

class Program

{

   static void Main(string[] args)

   {

       Gen<int[], string> obj = new Gen<int[], string>();

 

   }

}

 

}

 

 

Универсальные методы.

Универсальный метод – это метод, объявляемый с параметрами типа, как в следующем примере.

namespace ConsoleApplication1

{

class Program

{

   static void Swap<T>(ref T lhs, ref T rhs)

   {

       T temp;

       temp = lhs;

       lhs = rhs;

       rhs = temp;

   }

 

 

   static void Main(string[] args)

   {

       int a = 1;

       int b = 2;

 

       Swap<int>(ref a, ref b);

       System.Console.WriteLine(a + " " + b);

 

 

   }

}

}

В универсальном классе методы, не являющиеся универсальными, могут обращаться к параметрам типа уровня класса

namespace ConsoleApplication1

{

 

class SampleClass<T>

{

   public void Swap(ref T lhs, ref T rhs) {

       T temp;

       temp = lhs;

       lhs = rhs;

       rhs = temp;

   }

}

 

class Program

{

   static void Main(string[] args)

   {

       SampleClass<int> obj = new SampleClass<int>();

       int a = 1;

       int b = 7;

 

       obj.Swap(ref a, ref b);

       System.Console.WriteLine(a + " " + b);

 

 

   }

}

}

Если определить универсальный метод, принимающий те же параметры типа, что и содержащий класс, компилятор создаст предупреждение

Предупреждение 1 Имя параметра-типа "T" совпадает с именем параметра-типа внешнего типа "ConsoleApplication1.SampleClass<T>".  

namespace ConsoleApplication1

{

 

class SampleClass<T>

{

   public void Swap<T>(ref T lhs, ref T rhs) {

       T temp;

       temp = lhs;

       lhs = rhs;

       rhs = temp;

   }

}

 

class Program

{

   static void Main(string[] args)

   {

       SampleClass<int> obj = new SampleClass<int>();

       int a = 1;

       int b = 7;

 

       obj.Swap(ref a, ref b);

       System.Console.WriteLine(a + " " + b);

 

 

   }

}

}

Чтобы с параметрами типа в методах можно было выполнять более специализированные операции, используйте ограничения. Текущую версию Swap<T>, которая теперь имеет имя SwapIfGreater<T>, можно использовать только с аргументами типа, реализующими IComparable<T>.

void SwapIfGreater<T>(ref T lhs, ref T rhs) where T : System.IComparable<T>{ T temp; if (lhs.CompareTo(rhs) > 0) {   temp = lhs;   lhs = rhs;   rhs = temp; }}

namespace ConsoleApplication1

{

class SampleClass<T>

{

   public void Swap(ref T lhs, ref T rhs)

   {

       T temp;

       temp = lhs;

       lhs = rhs;

       rhs = temp;

   }

   public void SwapIfGreater<T>(ref T lhs, ref T rhs) where T : System.IComparable<T>

   {

       T temp;

       if (lhs.CompareTo(rhs) > 0)

       {

           temp = lhs;

           lhs = rhs;

           rhs = temp;

       }

   }

}

 

class Program

{

   static void Main(string[] args)

   {

       SampleClass<int> obj = new SampleClass<int>();

       int a = 11;

       int b = 7;

       obj.SwapIfGreater<int>(ref a, ref b);

       System.Console.WriteLine(a + " " + b);

   }

}

}





Универсальные интерфейсы.

Пример код будет работать

interface IBaseInterface<T> { }

 

class SampleClass : IBaseInterface<string> { }

 

class Program

{

   static void Main(string[] args)

   {

          

   }

}

ошибка

interface IBaseInterface<T> { }

 

class SampleClass : IBaseInterface<T> { }

 

нет ошибки

interface IBaseInterface<T> { }

 

class SampleClass<T> : IBaseInterface<T> { }

Отражение

Классы в пространстве имен System.Reflection вместе с System.Type позволяют получать сведения о загруженных сборках и определенных в них типах, таких как классы, интерфейсы и типы значений. Отражение можно также использовать для создания экземпляров типов во время выполнения, для вызова этих экземпляров и получения доступа к ним.

 

Универсальные типы и отражение

Определить ограничение базового типа и ограничения интерфейса параметра универсального типа с использованием метода GetGenericParameterConstraints

Пример

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Reflection;

 

namespace ConsoleApplication1

{

 

interface IPrintable

{

   void Print();

}

interface IComparable<TSecond>

{

   int CompareTo(TSecond value);

}

class Pair<TSecond>

   where TSecond : class, IPrintable, IComparable<TSecond>

{

}

 

class Program

{

   static void Main(string[] args)

   {

 

       Type t = typeof(Pair<>);

       Console.WriteLine("\r\n {0}", t);

       Console.WriteLine(" Is this a generic type? {0}",

           t.IsGenericType);

       Console.WriteLine(" Is this a generic type definition? {0}",

           t.IsGenericTypeDefinition);

 

       // Get the generic type parameters or type arguments.

       Type[] typeParameters = t.GetGenericArguments();

 

       Console.WriteLine(" List {0} type arguments:",

           typeParameters.Length);

          

       foreach (Type tParam in typeParameters)

       {

              

              Console.WriteLine( tParam.Name);

              

       }

 

       Type[] typeConstraint = typeParameters[0].GetGenericParameterConstraints();

 

       foreach (Type iConstraint in typeConstraint)

       {

           if (iConstraint.IsInterface)

           {

               Console.WriteLine("    Interface constraint: {0}",

                   iConstraint);

           }

       }

 

   }

}

}

Объявление типа

Программы на языке C# используют объявление типа для создания нового типа.

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

Пример, объявление класса.

объявление класса это объявление типа, для создания нового класса.

class-declaration:
attributesopt class-modifiersopt partialopt class identifier type-parameter-listopt
            class-baseopt type-parameter-constraints-clausesopt class-body ;opt

opt – необязательно

class identifier class-body - обязательно указывать при определении класса.

Объявление типа включает следующие сведения:

· attributes – все определенные в типе атрибуты;

· class-modifiers – видимость типа;

· Ключевое слово, определяющее тип. В данном примере это class;

· identifier – имя типа;

· type-parameter-list  – список параметров типа;

· class-base базовый тип данного типа;

· type-parameter-constraints-clauses – ограничения параметров типа

· class-body - тело типа.

 

Атрибуты предоставляют дополнительные пользовательские метаданные.

Видимость типа и модификаторы доступа к члену типа. При помощи модификаторов доступа можно задать следующие пять уровней доступности:

Объявленная доступность Значение
public Доступен всем методам во всех сборках.
protected Доступен только методам в определяющем типе и вложенных в него типах или одном из его производных типов независимо от сборки
internal Доступен только методам в определяющей сборке.
protected internal Доступ ограничен текущей сборкой или типами, которые являются производными от содержащего класса.
private(закрытый) Доступен только методам в определяющем типе и вложенных в него типах.

Ключевое слово, определяющее тип.  

Например, class, struct, interface, enum, delegate.

Имя типа. Система общих типов накладывает на имена два следующих ограничения:

Все имена кодируются в виде строк, состоящих из символов Юникода (16-разрядная кодировка).

Имена не могут иметь внедренное (16-разрядное) значение 0x0000.

Список параметров типа. Классы, структуры, интерфейсы и делегаты могут быть определены как пользовательские универсальные типы (универсальные шаблоны).

Пример

public class GenericList<T>

 <T> – type-parameter-list (список параметров типа).

Базовый тип данного типа. Тип может наследовать значения и поведение другого типа. Система общих типов не разрешает наследование от нескольких базовых типов. Все типы являются производными от System.Object, что позволяет:

сравнивать два экземпляра

получать хэш-код экземпляра

определять тип экземпляра

получать копию экземпляра

получать состояние объекта в виде строки



Все интерфейсы, реализованные этим типом.

Ограничения параметров типа (type-parameter-constraints-clauses).

Тело типа. Например, в классе, в теле класса задаются данные и поведение. Поля, свойства, методы и события в классе обозначаются термином члены класса.

Члены типа.

В типе можно определить следующие члены: (Джеффри Рихтер CLR via C#. Программирование на платформе Microsoft .NET Framework 2.0 на языке C# Питер, Русская Редакция 2007)

Константа. Идентификатор, определяющий постоянную величину. Константы связаны с типом, а не с экземпляром.

Поле. Представляет неизменяемое или изменяемое значение. Поле может быть статическим – тогда оно является частью типа и экземплярным ­ ­­­- тогда оно является частью объекта.

Метод. Представляет собой функцию, выполняющую операции, которые изменяют или запрашивают состояние типа (статический метод) или объекта (экземплярный метод).

Конструктор экземпляра– метод, используемый для инициализации полей экземпляра при его создании.

Конструктор типа – метод, используемый для инициализации статических полей типа.

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

Перегруженный оператор.

Оператор преобразования.

Событие.

Тип. Позволяет определять другие вложенные в него типы.

Объявления для типов:

Class declarations

class-declaration:
attributesopt class-modifiersopt partialopt class identifier type-parameter-listopt
            class-baseopt type-parameter-constraints-clausesopt class-body ;opt



Struct declarations

struct-declaration:
attributesopt struct-modifiersopt partialopt struct identifier type-parameter-listopt
            struct-interfacesopt type-parameter-constraints-clausesopt struct-body ;opt



Interface declarations

interface-declaration:
attributesopt interface-modifiersopt partialopt interface identifier type-parameter-listopt
            interface-baseopt   type-parameter-constraints-clausesopt interface-body ;opt



Enum declarations

enum-declaration:
attributesopt enum-modifiersopt enum identifier enum-baseopt enum-body ;opt


Delegate declarations

delegate-declaration:
attributesopt delegate-modifiersopt delegate return-type identifier type-parameter-listopt
            ( formal-parameter-listopt ) type-parameter-constraints-clausesopt ;

Ключевые слова, которые влияют на определение типа или члена типа:

Ключевое слово Тип Метод/Свойство/Событие Константа/Поле
abstract экземпляры не могут быть созданы Необходимо переопределить и реализовать в производном типе – только после этого можно создавать экземпляры производного типа Запрещено
virtual Запрещено Член можно переопределить в производном типе Запрещено
override запрещено Член переопределяется в производном типе Запрещено
sealed Не может использоваться как базовый тип Член нельзя переопределить в производном типе. Применяется к методу, переопределяющему виртуальный метод. Запрещено
new

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

Модификаторы (modifier) типов.

class-modifier struct-modifier: interface-modifier enum-modifier: delegate-modifier:
new public protected internal private abstract sealed static new public protected internal private new public protected internal private   new public protected internal private new public protected internal private

                                                                                                 

Члены (member) типов.

class-member struct -member interface -member enum -member delegate -member
constant field method property event indexer operator constructor destructor static-constructor type constant field method property event indexer operator constructor   static-constructor type     Члены структур не могут использоваться с модификатором protected, потому что наследование от структуры невозможно.       interface-method interface-property interface-event interface-indexer   Члены интерфейса всегда являются открытыми, поскольку целью интерфейса является предоставление возможности доступа к классу или структуре другим типам. attributesopt identifier = constant-expression   Члены перечисления всегда являются открытыми, и модификаторы доступа не применяются  


Классы .

(Из спецификации ) A class is a data structure that may contain data members (constants and fields), function members (methods, properties, events, indexers, operators, instance constructors, destructors and static constructors), and nested types. Class types support inheritance, a mechanism whereby a derived class can extend and specialize a base class.

 

Класс — это структура данных, которая может содержать члены данных (константы и поля), члены функций (методы, свойства, события, индексаторы, операторы, конструкторы экземпляров, деструкторы и статические конструкторы) и вложенные типы. Типы классов поддерживают наследование — механизм, в котором производный класс может расширять и сужать базовый класс

 

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

Class declarations

class-declaration:
attributesopt class-modifiersopt partialopt class identifier type-parameter-listopt
            class-baseopt type-parameter-constraints-clausesopt class-body ;opt

 

Объявление класса состоит из необязательного набора атрибутов, затем следует необязательный набор модификаторов класса, необязательный модификатор partial, ключевое слово class и идентификатор, именующий класс, затем следует необязательный список параметров типа, необязательная спецификация базы класса, необязательный набор предложений ограничений параметров типа, тело класса и необязательная точка с запятой.

Объявление класса не предоставляет предложения ограничений параметров типа, если не предоставляется список параметров типа.

Объявление класса, предоставляющее список параметров типа, является объявлением универсального класса.



Модификаторы класса.

Объявление класса может включать последовательность модификаторов класса:

new
public
protected
internal
private
abstract
sealed

static
Появление в объявлении класса одного и того же модификатора несколько раз является ошибкой времени компилирования. Модификатор new допускается во вложенных классах. Модификаторы public, protected, internal и private управляют доступностью класса. В зависимости от контекста, в котором возникает объявление класса, некоторые из данных модификаторов могут быть запрещены. По умолчанию модификатором класса является модификатор internal. Пример.

using System;

 

namespace ConsoleApplication11

{

class myclass

{

 

}

class Program

{

   static void Main(string[] args)

   {

       myclass obj = new myclass();

   }

}

}

 

Пример. Объявление класса с модификатором private (protected). Данный код приведет к ошибке:

using System;

 

namespace ConsoleApplication11

{

private class myclass

{

 

}

class Program

{

   static void Main(string[] args)

   {

       myclass obj = new myclass();

   }

}

}

Ошибка     1     Элементы, определенные в пространстве имен, не могут объявляться в явном виде как частные, защищенные или защищенные внутренние           

Следующий код не приведет к ошибке. Здесь класс myclass объявленный с модификатором private является вложенным классом.

Using System;

 

namespace ConsoleApplication11

{

class Program

{

   private class myclass

   {

 

   }

   static void Main(string[] args)

   {

       myclass obj = new myclass();

   }

}

}

 

 








Абстрактные классы .

Модификатор abstract используется для указания незавершенности класса и необходимости его использования только в качестве базового класса. Использование оператора new для создания экземпляра  абстрактного класса является ошибкой времени компилирования.

Пример. Следующий код приведет к ошибке: 

using System;

 

namespace ConsoleApplication11

{

   

class Program

{

   abstract class myclass

   {

 

   }

   static void Main(string[] args)

   {

       myclass obj = new myclass();

   }

}

}

Ошибка 1 Невозможно создать экземпляр абстрактного класса или интерфейса.

Можно иметь переменные и значения, имеющие типы времени компиляции, являющиеся абстрактными типами, такие переменные и значения будут обязательно иметь значение null или будут содержать ссылки на экземпляры неабстрактных классов, производных от абстрактных типов.

Пример.

Нельзя создать экземпляр абстрактного класса Brush

Код

Brush mybrash = new Brush();

Приведет к ошибке.

Можно создать переменную типа Brush, которая будет содержать ссылку, например, на экземпляр неабстрактного класса SolidBrush, производного от абстрактного класса Brush:

 

 

 

private void Form1_Paint(object sender, PaintEventArgs e)

{

Brush mybrash = new SolidBrush(Color.Chocolate);

Graphics g = e.Graphics;

g.FillEllipse(mybrash, 100, 100, 30, 30);

}

 

Если неабстрактный класс является производным от абстрактного класса, неабстрактный класс должен включать фактические реализации всех унаследованных абстрактных членов, таким образом переопределяя данные абстрактные члены. Рассмотрим следующий пример:

using System;

 

namespace ConsoleApplication12

{

abstract class myCar

{

   abstract public void DescribeCar();

}

   

class Car : myCar

{

   public override void DescribeCar()

   {

       System.Console.WriteLine(“Four wheels and an engine.”);

   }

}

 

class TestClass

{

   static void Main()

   {

       Car car1 = new Car();

       car1.DescribeCar();

       System.Console.WriteLine(«----------«);

 

   }

}

}

 

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

Класс Car переопределяет DescribeCar и обеспечивает фактическую реализацию. Так как в классе Car нет абстрактных членов, класс Car может быть (но не должен) быть абстрактным.

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

Запечатанные классы .

Модификатор sealed используется для предотвращения создания из него производных классов. Если запечатанный класс указывается в качестве базового класса другого класса, возникает ошибка времени компилирования. Пример:

class A { }

sealed class B : A { }

class C : B { }

Ошибка     1     нельзя наследовать от запечатанного типа

Запечатанный класс не может быть также абстрактным классом.

Модификатор sealed в первую очередь используется для предотвращения непреднамеренного создания из него производных классов. Так как запечатанный класс не имеет производных классов, можно преобразовать вызовы виртуальной функции-члена экземпляров запечатанного класса в невиртуальные вызовы. Пример:

class X

{

   protected virtual void F() { Console.WriteLine("X.F"); }

   protected virtual void F2() { Console.WriteLine("X.F2"); }

}

class Y : X

{

   sealed protected override void F() { Console.WriteLine("Y.F"); }

   protected override void F2() { Console.WriteLine("X.F3"); }

}

class Z : Y

{

   // Ошибка 1 : невозможно переопределить наследуемый член поскольку он запечатан

   // protected override void F() { Console.WriteLine("C.F"); }

   // Overriding F2 is allowed.

   protected override void F2() { Console.WriteLine("Z.F2"); }

}

Статические классы .

 

Класс можно определить с ключевым словом static

Не реализует никаких интерфейсов.

Можно определять только статические члены.

Статический класс не может содержать члены с объявленной доступностью protected или protected internal.

Нельзя создавать экземпляры статического класса.

Доступ к членам статического класса осуществляется с использованием имени класса

Класс нельзя использовать в качестве параметра метода или локальной переменной.

Статический класс не может содержать модификатор sealed или abstract.

Нарушение данных ограничений приводит к ошибке времени компилирования.

Статический класс не имеет конструкторов экземпляров.

Члены статического класса автоматически не являются статическими, а объявления членов должны явно включать модификатор static (исключая константы и вложенные типы).

Тело класса

Тело класса для класса определяет члены этого класса.

class-body:
{ class-member-declarationsopt }

тело_класса:
{ объявления_члена_классанеобязательно }



Дата: 2019-03-05, просмотров: 270.