Студопедия

КАТЕГОРИИ:

АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника


Верификация и ограничения




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

 

private static Boolean MethodTakingAnyType<T>(T o)

{

T temp = o;

Console.WriteLine(o.ToString());

Boolean b = temp.Equals(o);

return b;

}

 

Здесь объявляется временная переменная (temp) типа Т, а затем выполняется несколько операций присвоения переменных и несколько вызовов методов. Он работает с любым типом T — ссылочным, значимым, перечислимым, типом-интерфейсом или типом-делегатом, существующим типом или типом, который определят в будущем, — потому что любой тип поддерживает присвоения и вызовы методов, определенных в Object (например, ToString и Equals).

Вот еще метод:

 

private static T Min<T>(T o1, T o2)

{

if (o1.CompareTo(o2) < 0)

return o1;

return o2;

}

 

Метод Min пытается через переменную o1 вызвать метод CompareTo. Но многие типы не поддерживают метод CompareTo, поэтому компилятор C# не в состоянии скомпилировать этот код и обеспечить, чтобы после компиляции метод смог работать со всеми типами. При попытке скомпилировать приведенный выше код появится сообщение об ошибке:

 

«Error CS0117: "T does not contain a definition for ' CompareTo »

«ошибка CS0117: T не содержит определение метода CompareTo»

 

Поэтому может показаться, что при использовании обобщений можно лишь объявлять переменные обобщенного типа, назначать переменные, вызывать методы, определенные Object, и все! Но ведь в таком случае от обобщений пользы мало. К счастью, компиляторы и CLR поддерживают механизм под названием ограничения (constraints), благодаря которому обобщения успешно «реабилитируются».

Ограничение позволяет сузить перечень типов, которые можно передать в обобщенном аргументе, и расширяет возможности по работе с этими типами. Вот новый вариант метода Min, который задает ограничение (выделено серым):

 

private static T Min<T>(T o1, T o2) where T : IComparable<T>

{

if (o1.CompareTo(o2) < 0)

return o1;

return o2;

}

 

Маркер where в C# сообщает компилятору, что указанный в T тип должен реализовывать обобщенный интерфейс IComparable того же типа (T). Благодаря этому ограничению компилятор разрешает методу вызвать метод CompareTo, потому что последний определен в интерфейсе IComparable<T>.

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

 

«Error CS0309: The type 'object' must be convertible to 'System.IComparable<object>' in order to use it as a parameter T in the generic type or method 'Program.Min<T>(T, T)'|»

«Ошибка CS0309: тип object необходимо преобразовать в System.IComparable<object>, чтобы его можно было использовать в качестве параметра T в обобщенном типе или методе Program.Min<T>(T, T)».

 

private static void CallMin()

{

Object o1 = "Jeff", o2 = "Richter";

Object oMin = Min<Object>(o1, o2); // Ошибка CS0309.

}

 

Компилятор выдает эту ошибку, потому что System.Object не реализует интерфейс IComparable<Object>. Честно говоря, System.Object вообще не реализует никаких интерфейсов.

Вы получили общее представление об ограничениях и их работе. Познакомимся с ними поближе. Ограничения можно применять к параметрам-типам как обобщенных типов, так и обобщенных методов (как показано в методе Min). CLR не поддерживает перегрузку по именам параметров-типов или ограничений. Перегрузка типов и методов выполняется только по арности. Покажу это на примере.

 

// Можно определить следующие типы:

internal sealed class AType { }

internal sealed class AType<T> { }

internal sealed class AType<T1, T2> { }

 

// Ошибка: конфликт с AType<T>, у которого нет ограничений,

internal sealed class АТуре<Т> where T : IComparable<T> {}

 

// Ошибка: конфликт с АТуре<Т1, Т2>.

internal sealed class АТуре<ТЗ, Т4> {}

 

internal sealed class AnotherType

{

// Можно определить следующие методы:

private static void M() {}

private static void М<Т>() { }

private static void M<T1, T2>() { }

 

// Ошибка: конфликт с M<T>, у которого нет ограничений.

private static void М<Т>() where T : IComparable<T> {}

 

// Ошибка: конфликт с М<Т1, Т2>.

private static void М<ТЗ, Т4>() {}

}

 

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

 

internal class Base

{

public virtual void M<T1, T2>()

where T1 : struct

where T2 : class { }

}

 

internal sealed class Derived : Base

{

public override void M<T3, T4>()

where T3 : EventArgs // Ошибка.

where T4 : class { } // Ошибка.

}

 

При компиляции этого кода появится сообщение об ошибке:

 

«Error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method so cannot be specified directly»

«Ошибка CS0460: ограничения для методов интерфейсов с переопределением и явной реализацией наследуются от базового метода и поэтому не могут быть заданы явно».

 

Если из метода М<ТЗ, Т4> класса Derived убрать две строки where, код успешно скомпилируется. Заметьте: разрешается переименовывать параметры-типы (в этом примере 77 изменено на ТЗ, а 72 на Т4), но изменять (и даже задавать) ограничения нельзя.

Теперь поговорим о различных типах ограничений, которые компилятор и CLR позволяют применять к параметрам-типам. К параметру-типу могут применяться следующие ограничения: основное, дополнительное и/или ограничение конструктора. Речь о них пойдет в следующих трех разделах.


Поделиться:

Дата добавления: 2015-04-21; просмотров: 80; Мы поможем в написании вашей работы!; Нарушение авторских прав





lektsii.com - Лекции.Ком - 2014-2024 год. (0.006 сек.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав
Главная страница Случайная страница Контакты