// MyGenericClass получается из МуВаsе и реализует ISomeInterface,

// а вложенные элементы должны быть структурами.

public class MyGenericClass‹T›: MyBase, ISomeInterface where T: struct {…}

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

// ‹К› должен иметь конструктор, заданный по умолчанию,

// а ‹Т› должен реализовывать открытый интерфейс IComparable.

public class MyGenericClass‹K, T› where K: new where T: IComparable‹T› {…}

Если вы хотите изменить тип CarCollection‹T› так, чтобы в него можно было поместить только производные от Car, вы можете записать следующее.

public' class CarCollection‹T›: IEnumerable‹T› where T: Car {

 public void PrintPetName(int роs) {

  // Поскольку теперь все элементы должны быть из семейства Car,

  // свойство PetName можно вызывать непосредственно.

  Console.WriteLine(arCars[pos].PetName);

 }

}

При таких ограничениях на CarCollection‹T› реализация PrintPetName становится очень простой, поскольку теперь компилятор может предполагать, что ‹Т› является производным от Car. Более того, если указанный пользователем параметр типа не совместим с Car, будет сгенерирована ошибка компиляции.

// Ошибка компиляции!

CarCollection‹int› myInts = new CarCollection‹int›;

Вы должны понимать, что обобщенные методы тоже могут использовать ключевое слово where. Например, если нужно гарантировать, чтобы методу Swap, созданному в этой главе выше, передавались только типы, производные от System. ValueType, измените свой программный код так.

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

static void Swap‹T›(ref Т а, ref T b) where T: struct {

 …

}

Следует также понимать то, что при таком ограничении метод Swap уже не сможет переставлять строковые типы (поскольку они являются ссылочными).

<p>Отсутствие поддержки ограничений при использовании операций</p>

При создании обобщенных методов для вас может оказаться сюрпризом появление ошибок компилятора, когда с параметрами типа используются операции C# (+, -, *, == и т.д.). Например, я уверен, вы сочли бы полезными классы Add, Subtract, Multiply и Divide, способные работать с обобщенными типами.

// Ошибка компиляции!

// Нельзя применять операции к параметрам типа!

public class BasicMath‹T› {

 public T Add(T arg1, T arg2) { return arg1 + arg2; }

 public T Subtract(T arg1, T arg2) { return arg1 – arg2; }

 public T Multiply(T arg1, T arg2) { return arg1 * arg2; }

 public T Divide(T arg1, T arg2) { return arg1 / arg2; }

}

Как ни печально, этот класс BasicMath‹T› не компилируется. Это может показаться большим ограничением, но не следует забывать, что обобщения являются обобщениями. Конечно, тип System.Int32 может прекрасно работать с бинарными операциями C#. Однако, если, например, ‹T› будет пользовательским классом иди типом структуры, компилятор не сможет сделать никаких предположений о характере перегруженных операций +, -, * и /. В идеале C# должен был бы позволять обобщенному типу ограничения с использованием операций, например, так.

// Только для иллюстрации!

// Этот программный код не является допустимым в C# 2.0.

public class BasicMath‹T› where T: operator +, operator -, operator *, operator / {

 public T Add(T arg1, T arg2) { return arg1 + arg2; }

 public T Subtract(T arg1, T arg2) { return arg1 – arg2; }

 public T Multiply(T arg1, T arg2) { return arg1 * arg2; }

  public T Divide(T arg1, T arg2) { return arg1 / arg2; }

}

Перейти на страницу:

Похожие книги