Po předchozím pohledu na nový, a možná pro některé kontroverzní, datový typ Tuple se dnes poohlédneme po nových vylepšeních jazyka. A sice po kovarianci a kontravarianci.
reklama
Po předchozím pohledu na nový, a možná pro některé kontroverzní, datový typ Tuple se dnes poohlédneme po nových vylepšeních jazyka. A sice po kovarianci a kontravarianci.
Každý, objektově programující vývojář, zná možnost do proměnné s typem nadřazené třídy vložit libovolného potomka. Můžete tedy to proměnné typu Exception přiřadit DataException apod. A díky faktu, že typ object je nejvýše v hierarchii, je možné do proměnné tohoto typu přiřadit cokoli (nicméně nemusí to být vždy optimální řešení). Dalo by se očekávat, že např. do IEnumerable objektů půjde přiřadit i IEnumerable řetězců. Ale ouha, nejde to. Alespoň ne dnes.
Od C# 4 a .NET 4.0 bude toto přiřazení možné a kompilátor nebude na tomto kódu protestovat:
static void Main(string[] args)
{
IEnumerable<object> objects;
objects = GetStrings();
}
private static IEnumerable<string> GetStrings()
{
return new string[] { "a", "b", "c" };
}
Velmi jednoduché. To co jsme nyní okusili je tzv. kovariance (zde na IEnumerable). Rozhraní IEnumerable je nově definováno jako:
interface IEnumerable<out T> : IEnumerable
Všimněte si klíčového slova „out“ před generickým typem T. Nové místo pro toto již zavedené klíčové slovo říká, že typ T se může v tomto rozhraní vyskytovat pouze jako výstupní. Ve spojení s LINQem dostáváme ještě větší volnost ve využívání těchto extension metod. Např. volání:
IEnumerable<object> objects = new object[4];
GetStrings().Union(objects);
Je naprosto v pořádku a bez dodatečného přetypování.
Obráceným případem je kontravariance. K deklaraci se používá klíčového slova „in“. Často uváděným příkladem je rozhraní IComparer<in T>. Tento zápis nám říká, že generický typ T do rozhraní vstupuje. Máte-li pak IComparer<object>, můžete jej použít i pro třeba IComparer<string>, neboť umím-li porovnávat objekty, a string je objekt, pak jistě umím porovnávat i řetězce.
Možná vás napadlo, jestli je možné použít „in“ a „out“ najednou. Je to možné a některé typy v .NET 4 tuto možnost využívají. Například Func<T, TResult> je definován jako:
delegate TResult Func<in T, out TResult>(T arg);
Co může být zajímavé, je fakt, že Func<object, string> může být použit i jako Func<string, object>. Proto kód:
static void Main(string[] args)
{
Func<string, object> fce;
fce = Fce;
}
private static string Fce(object s)
{
return s.ToString();
}
Bez problému funguje. Ale obráceně to přirozeně možné není.
Každá nová vlastnost má i svá omezení. Především ko- a kontravariantní parametry je možné definovat pouze pro delegáty a rozhraní. Dalším omezením je nutnost používat pouze typy, mezi kterými existuje referenční konverze. Například konverze z double na object není referenční, ale jde pouze o boxing. Pozor také na, možná trochu nekonzistentní, chování pro anonymní funkce (teda i lambda funkce/výrazy), kde variance není podporována (velmi pěkně rozebráno v [1])
Variance na delegátech a rozhraních je novinka, kterou jistě přivítá každý vývojář, neboť může ušetřit psaní několika zbytečných řádků s přetypováním. Doufejme jen však, že nebude zdrojem „magického“ používání.
[1]
http://blog.renestein.net/Drobnaacute+Poznaacutemka+Ke+Kontravarianciacute+Delegaacutetů+V+C.aspx