Type system covariance and contravariance
Полагаю, многие разработчики не знают, что такое ковариантность/контравариарность системы типов. К примеру, в версии 1.5 языка Java появилась возможность использовать тип класса-наследника в объявлении замещающей функции:
0: /** 1: * 2: */ 3: package com.chabster.covariance; 4: 5: /** 6: * @author Chabster 7: */ 8: public class Container 9: { 10: class A 11: { 12: A some() { 13: return (this); 14: } 15: } 16: 17: class B extends A 18: { 19: @Override 20: B some() { 21: return (this); 22: } 23: } 24: 25: }
В С++ такая возможность существует уже давно, а вот в C# все еще отсутствует. Полагаю, это связано с нестыковкой сигнатур методов в C# и IL. Вот, что говорит по этому поводу Standard ECMA-335 Common Language Infrastructure (CLI) 4th edition (June 2006):
A method signatures is composed of
- a calling convention,
- the number of generic parameters, if the method is generic,
- a list of zero or more parameter signatures—one for each parameter of the method—and,
- a type signature for the result value, if one is produced.
Получается, что IL код различает методы, которые отличаются лишь типом возврата, а C# - нет. Как и любой существующий на данный момент управляемый .NET-язык. С другой стороны этот недостаток можно элементарно восполнить. Вариант реализации предлогаю расценивать, как домашнее задание для читателя :).
В некоторых моментах C#, все же, поддерживает ковариантность и контравариантнось. Но для начала выясним что это такое.
Ковариантность (covariance) и контравариантнось (contravariance)
Ковариантностью называют сохранение формы при преобразовании. Соответсвенно, ковариантным называется преобразование, сохраняющее форму (свойства).
Например, оператор F(x) = x*2
является ковариантным касательно отношения % (делимость).
Т.е. из x%y следует, что F(x)%F(y).
Контравариантностью называют обращение формы при преобразовании. Соответсвенно, контравариантным называется преобразование, обращающее форму.
Например, оператор F(x) = -x
является контравариантным касательно отношения >
(больше). Т.е. из x>y следует, что F(x)<F(y).
Оба этих понятия нельзя правильно обобщить поскольку в каждой науке оно выглядит по-своему. Рассмотрим их в самой интересной для нас области - программирование!
Ковариантность и контравариантнось в системе типов языков программирования
Рассмотрим пример C# кода:
class A { A[] aArr = new B[] { }; } class B : A { }
Пусть « обозначает отношение is a
для типов. Поэтому B«A. Оператор Arrize({T}) = {T[]}
является
ковариантным касательно отношения is a
для ссылочных типов т.к. из B«A следует B[]«A[].
Возвращаясь к первому Java примеру можно сказать, что оператор Methodize({SuperT virtMethod(T1,T2,...)}) = {SubT virtMethod(T1,T2,...)}
является ковариантным касательно отношения override между методами.
Ковариантность и контравариантнось в системе типов C#
В C# есть ковариация, связанная с делегатами:
class A { } class B : A { } delegate A SomeDelegate(); static B SomeMethod() { return (null); } static SomeDelegate sd = SomeMethod;
Типы возвращаемых значений метода и делегата различны, но совместимы.
В C# есть и контравариация, связанная с делегатами:
class A { } class B : A { } delegate void SomeDelegate(B b); static void SomeMethod(A a) { } static SomeDelegate sd = SomeMethod;
Сигнатуры метода и делегата различны, но совместимы, правда, уже в обратном направлении.
Это все, конечно, хорошо, вот только...
class A { } class B : A { } delegate T SomeDelegate<T>(); static B SomeMethod() { return (null); } static SomeDelegate<B> sdB = SomeMethod; static SomeDelegate<A> sdA = sdB;
... последняя строчка вызывает ошибку компиляции.
В целом, вариантность - очень полезный механизм, если им правильно пользоваться.