Обобщенные системные интерфейсы

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

Например, классы и структуры могут реализовывать IComparable и/или IComparable, равно как и IEquatable. Естественно, IComparable — более безопасная в отношении типов версия IComparable, и потому ей следует отдавать предпочтение, когда это возможно.

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

В пространстве имен System.Collections .Generic также определен целый набор интерфейсов, представляющих собой обобщенные версии интерфейсов из пространства имен System.Collections. В их число входят ICollection, IDictionary и IList. Два из них имеют специальное назначение: IEnumerator и IEnumerable2.

В команде разработчиков из Microsoft решили, что будет хорошей идеей наследовать IEnumerator от IEnumerator, a IEnumerable — от IEnumerable. Это решение вызвало немало споров. Андерс Хейлсберг (Anders Hejlsberg), “отец” языка С#, указывает, что IEnumerable унаследован от IEnumerable, потому что может это делать.

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

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

Это еще один пример ковариантности.

Это оправдывает утверждение о том, что IEnumerable может наследоваться от IEnumerable, a IEnumerator — от IEnumerator только потому, что они могут это делать. Один из разработчиков в Microsoft, работавших над библиотекой .NET Framework, говорил, что IEnumerable и IEnumerator реализованы таким образом для того, чтобы компенсировать недостаток ковариантности обобщений. Не мудрено сбиться с толку.

Однако это все стало менее актуальным с тех пор, как в С# 4.0 появился синтаксис, позволяющий реализовывать ковариантные обобщенные интерфейсы.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System;
using System.Collections.Generic-public class MyContainer<t> : IEnumerable<t>
{
public void Add ( T item )  {
impl.Add( item ) ;
}
public void Add<r> ( MyContainer<r> otherContainer, Converter<r, T> converter )
{
foreach( R item in otherContainer )  {
impl.Add( converter(item) );
}
public IEnumerator<t> GetEnumerator ()  {
foreach ( T item in impl )  {
yield return item;
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator()  {
return GetEnumerator ();
}
private List<t> impl = new List<t>();
}

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100