Для обеспечения безопасности типов C # 4.0 поддерживает ковариацию / контравариантность ТОЛЬКО для параметров типа, отмеченных как in или out.
Если бы это распространялось на классы, вам также нужно было бы пометить параметры типа в нашем выходе, и это в конечном итоге стало бы очень ограничительным. Скорее всего, поэтому разработчики CLR решили не допустить этого. Например, рассмотрим следующий класс:
public class Stack<T>
{
int position;
T[] data = new T[100];
public void Push (T obj) { data[position++] = obj; }
public T Pop() { return data[--position]; }
}
Было бы невозможно аннотировать T как на выходе, потому что T используется как во входных, так и в выходных позициях. Следовательно, этот класс никогда не может быть ковариантным или контравариантным - даже в C # поддерживаются параметры ковариантного / контравариантного типа для классов.
Интерфейсы прекрасно решают проблему. Мы можем определить два интерфейса, как показано ниже, и сделать так, чтобы Stack реализовал оба:
public interface IPoppable<out T> { T Pop(); }
public interface IPushable<in T> { void Push (T obj); }
Обратите внимание, что T ковариантен для IPoppable и контравариантен для IPushable. Это означает, что T может быть ковариантным или контравариантным - в зависимости от того, приводите ли вы к IPoppable или IPushable.
Другая причина того, что ковариация / контравариантность будет иметь ограниченное использование с классами, состоит в том, что она исключает использование параметров типа в качестве полей, потому что поля фактически допускают операции ввода и вывода. Фактически, было бы сложно написать класс, который бы делал что-либо полезное, с параметром типа, помеченным как in или out. Даже в простейшем случае написания ковариантной реализации Enumerable возникнет проблема - как бы с самого начала получить исходные данные в экземпляр?
person
Joe Albahari
schedule
29.09.2010