İşte bir miras hiyerarşisi kullanan basit bir örnek.
Basit sınıf hiyerarşisi göz önüne alındığında:
Ve kodda:
public abstract class LifeForm { }
public abstract class Animal : LifeForm { }
public class Giraffe : Animal { }
public class Zebra : Animal { }
Değişmezlik (yani, genel tip parametreleri * ile süslenmemiş *in
veya out
anahtar kelimeler)
Görünüşe göre, böyle bir yöntem
public static void PrintLifeForms(IList<LifeForm> lifeForms)
{
foreach (var lifeForm in lifeForms)
{
Console.WriteLine(lifeForm.GetType().ToString());
}
}
... heterojen bir koleksiyonu kabul etmelidir:
var myAnimals = new List<LifeForm>
{
new Giraffe(),
new Zebra()
};
PrintLifeForms(myAnimals); // Giraffe, Zebra
Ancak, daha türetilmiş tipte bir koleksiyonu geçmek başarısız olur!
var myGiraffes = new List<Giraffe>
{
new Giraffe(), // "Jerry"
new Giraffe() // "Melman"
};
PrintLifeForms(myGiraffes); // Compile Error!
cannot convert from 'System.Collections.Generic.List<Giraffe>' to 'System.Collections.Generic.IList<LifeForm>'
Neden? Genel parametre IList<LifeForm>
kovaryant olmadığından -
IList<T>
değişmez olduğundan, IList<LifeForm>
yalnızca parametrelenen türün T
olması gereken koleksiyonları (IList uygulayan) kabul ederLifeForm
.
Yöntemin uygulanması PrintLifeForms
kötü amaçlıysa (ancak aynı yöntem imzasına sahipse), derleyicinin geçişini engellemesinin nedeni List<Giraffe>
açıktır:
public static void PrintLifeForms(IList<LifeForm> lifeForms)
{
lifeForms.Add(new Zebra());
}
Yana IList
izin ekleme ya da elemanların çıkarılması, herhangi bir alt-sınıfı LifeForm
, böylece bir parametre ilave edilebilir lifeForms
, ve elde edilen tiplerinin herhangi toplama tipi ihlal edeceği yönteme geçirildi. (Burada, kötü niyetli yöntem eklemek girişiminde Zebra
To var myGiraffes
). Neyse ki, derleyici bizi bu tehlikeden korur.
Kovaryans (Parametrik tip süslemeli genel out
)
Kovaryans, değişmez koleksiyonlarla yaygın olarak kullanılır (yani bir koleksiyona yeni elemanların eklenemediği veya koleksiyondan çıkarılamadığı durumlarda)
Yukarıdaki örneğin çözümü, bir kovaryant jenerik toplama tipinin kullanılmasını sağlamaktır, örn. IEnumerable
(Olarak tanımlanmıştır IEnumerable<out T>
). IEnumerable
koleksiyona geçmek için herhangi bir yöntem yoktur ve out
kovaryansın bir sonucu olarak, alt tipine sahip herhangi bir koleksiyon LifeForm
artık yönteme geçirilebilir:
public static void PrintLifeForms(IEnumerable<LifeForm> lifeForms)
{
foreach (var lifeForm in lifeForms)
{
Console.WriteLine(lifeForm.GetType().ToString());
}
}
PrintLifeForms
Şimdi ile çağrılabilir Zebras
, Giraffes
ve herhangi IEnumerable<>
herhangi alt sınıfınınLifeForm
Çelişki (Parametrik tip süslemeli genel in
)
İşlevler parametre olarak iletildiğinde karşıtlık sıklıkla kullanılır.
Aşağıda Action<Zebra>
parametre olarak alan ve bilinen bir Zebra örneğinde çağıran bir işlev örneği verilmiştir:
public void PerformZebraAction(Action<Zebra> zebraAction)
{
var zebra = new Zebra();
zebraAction(zebra);
}
Beklendiği gibi, bu iyi çalışıyor:
var myAction = new Action<Zebra>(z => Console.WriteLine("I'm a zebra"));
PerformZebraAction(myAction); // I'm a zebra
Sezgisel olarak, bu başarısız olacaktır:
var myAction = new Action<Giraffe>(g => Console.WriteLine("I'm a giraffe"));
PerformZebraAction(myAction);
cannot convert from 'System.Action<Giraffe>' to 'System.Action<Zebra>'
Ancak, bu başarılı
var myAction = new Action<Animal>(a => Console.WriteLine("I'm an animal"));
PerformZebraAction(myAction); // I'm an animal
ve bu da başarılı olur:
var myAction = new Action<object>(a => Console.WriteLine("I'm an amoeba"));
PerformZebraAction(myAction); // I'm an amoeba
Neden? Çünkü Action
olarak tanımlanır Action<in T>
, yani bunun contravariant
için, yani, Action<Zebra> myAction
bu, myAction
"en", bir de olabilir Action<Zebra>
ki, ama daha az türetilmiş süper sınıf Zebra
da kabul edilebilir.
Bu başlangıçta sezgisel olmasa da (örneğin Action<object>
, bir parametre gerektiren nasıl geçirilebilir Action<Zebra>
?), Adımları kaldırırsanız, çağrılan fonksiyonun ( PerformZebraAction
) kendisinin veri geçirmekten sorumlu olduğunu (bu örnekte bir Zebra
örnek ) göreceksiniz. ) işleve - veriler arama kodundan gelmez.
Daha yüksek dereceli işlevleri bu şekilde kullanma tersine çevrilmiş yaklaşımı nedeniyle Action
, çağrıldığında, işlevin kendisi daha az türetilmiş bir tür kullanmasına rağmen, işleve (parametre olarak iletilir) Zebra
çağrılan daha türetilmiş örnektir zebraAction
.