Объявления универсального типа и метода могут дополнительно указывать ограничения параметра типа, включая предложения_ограничений_параметров_типа.
предложения_ограничений_параметров_типа:
предложение_ограничений_параметров_типа
предложение_ограничений_параметров_типа:
where параметр_типа : ограничения_параметров_типа
ограничения_параметров_типа:
первичное_ограничение
вторичные_ограничения
ограничение_для_конструктора
первичное_ограничение , вторичные_ограничения
первичное_ограничение , ограничение_для_конструктора
вторичные_ограничения , ограничение_для_конструктора
первичное_ограничение , вторичные_ограничения , ограничение_для_конструктора
первичное_ограничение:
тип_класса
class
struct
вторичные_ограничения:
тип_интерфейса
параметр_типа
вторичные_ограничения , тип_интерфейса
вторичные_ограничения , параметр_типа
ограничение_для_конструктора:
new ( )
Каждое предложение_ограничений_параметров_типа состоит из маркера where, следующих затем имени параметра типа, двоеточия и списка ограничений для данного параметра типа. Может существовать не более одного предложения where для каждого параметра типа, порядок перечисления предложений where не имеет существенного значения. Аналогично маркерам get и set в функции доступа к свойству, маркер where не является ключевым словом.
Список ограничений в предложении where может включать любые из представленных ниже компонентов в следующем порядке: одно первичное ограничение , одно или несколько вторичных ограничений и ограничение конструктора , new() .
Первичное ограничение может быть типом класса или ограничением ссылочного типа class или ограничением типа значения struct.
Вторичное ограничение может быть параметром_типа или типом_интерфейса.
Ограничение ссылочного типа указывает, что аргумент типа, используемый для параметра типа, должен быть типом ссылки. Все типы классов, интерфейсов, делегатов, массивов и параметры типов считаются ссылочными типами (см. ниже), удовлетворяющих данному ограничению.
Ограничение типа значения указывает, что аргумент типа, используемый для параметра типа, не должен быть типом, который может иметь значение null. Все типы структур, которые не могут иметь значение null, типы перечислений и параметры типа с ограничением типа значения удовлетворяют данному ограничению.
Параметр типа с ограничением типа значения также не может иметь ограничение_конструктора.
Первичное ограничение
Ограничение типа_класса должно удовлетворять следующим правилам.
· Типом должен быть тип класса.
· Тип не должен быть 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);
}
}
}
}
}
Дата: 2019-03-05, просмотров: 219.