İle <out T>
, arayüz referansını hiyerarşide yukarı doğru bir referans olarak ele alabilirsiniz.
İle <in T>
, arayüz referansını hiearchy'de aşağı doğru bir referans olarak ele alabilirsiniz.
Bunu daha ingilizce terimlerle açıklamaya çalışayım.
Diyelim ki hayvanat bahçenizden hayvanların bir listesini alıyorsunuz ve onları işlemeyi düşünüyorsunuz. Tüm hayvanların (hayvanat bahçenizdeki) bir adı ve benzersiz bir kimliği vardır. Bazı hayvanlar memeliler, bazıları sürüngenler, bazıları amfibiler, bazıları balık, vs. ama hepsi hayvan.
Bu nedenle, (farklı türdeki hayvanları içeren) hayvan listenizle, tüm hayvanların bir adı olduğunu söyleyebilirsiniz, bu nedenle tüm hayvanların adını almanız kesinlikle güvenli olacaktır.
Ancak, ya yalnızca balıkların bir listesine sahipseniz ve onlara hayvan gibi davranmanız gerekiyorsa, bu işe yarıyor mu? Sezgisel olarak çalışmalıdır, ancak C # 3.0 ve öncesinde bu kod parçası derlenmeyecektir:
IEnumerable<Animal> animals = GetFishes();
Bunun nedeni derleyici size niyetinde ne "bilmez" ya olmasıdır olabilir bunu aldıktan sonra, hayvanlar koleksiyonu ile yapmak. Tek bildiği gibi, IEnumerable<T>
bir nesneyi listeye geri koymanın bir yolu olabilir ve bu, balık olmayan bir hayvanı yalnızca balık içermesi gereken bir koleksiyona koymanıza olanak tanır.
Başka bir deyişle, derleyici buna izin verilmediğini garanti edemez:
animals.Add(new Mammal("Zebra"));
Dolayısıyla derleyici, kodunuzu derlemeyi tamamen reddeder. Bu kovaryans.
Kontravariance bakalım.
Hayvanat bahçemiz tüm hayvanları idare edebildiğinden, kesinlikle balıkları idare edebilir, bu yüzden hayvanat bahçemize biraz balık eklemeye çalışalım.
C # 3.0 ve öncesinde, bu derlemez:
List<Fish> fishes = GetAccessToFishes();
fishes.Add(new Fish("Guppy"));
Burada, derleyici olabilir yöntem döndürür rağmen Bu kod parçasını izin List<Animal>
sadece bu tip değişti eğer öyleyse, bütün balıklar hayvanlardır çünkü basitçe:
List<Animal> fishes = GetAccessToFishes();
fishes.Add(new Fish("Guppy"));
O zaman işe yarar, ancak derleyici bunu yapmaya çalışmadığınızı belirleyemez:
List<Fish> fishes = GetAccessToFishes();
Fish firstFist = fishes[0];
Liste aslında bir hayvan listesi olduğu için buna izin verilmiyor.
Dolayısıyla, ters ve eş varyans, nesne referanslarını nasıl ele aldığınız ve onlarla ne yapmanıza izin verildiğidir.
in
Ve out
bir ya da diğer olarak C anahtar # 4,0 spesifik işaretler arayüzü. İle in
, genel türü (genellikle T) girdi konumlarına yerleştirmenize izin verilir ; bu, yöntem bağımsız değişkenleri ve salt yazılır özellikler anlamına gelir.
İle out
, genel türü , yöntem dönüş değerleri, salt okunur özellikler ve çıkış yöntemi parametreleri olan çıktı konumlarına yerleştirmenize izin verilir .
Bu, kodla ne yapmak istediğinizi yapmanıza izin verecektir:
IEnumerable<Animal> animals = GetFishes();
List<T>
T üzerinde hem giriş hem de çıkış yönüne sahiptir, bu nedenle ne ortak değişken ne de ters varyanttır, ancak aşağıdaki gibi nesneler eklemenize izin veren bir arayüzdür:
interface IWriteOnlyList<in T>
{
void Add(T value);
}
bunu yapmanıza izin verir:
IWriteOnlyList<Fish> fishes = GetWriteAccessToAnimals();
IWriteOnlyList<Animal>
fishes.Add(new Fish("Guppy")); <-- this is now safe
İşte kavramları gösteren birkaç video:
İşte bir örnek:
namespace SO2719954
{
class Base { }
class Descendant : Base { }
interface IBibbleOut<out T> { }
interface IBibbleIn<in T> { }
class Program
{
static void Main(string[] args)
{
IBibbleOut<Base> b = GetOutDescendant();
IBibbleIn<Descendant> d = GetInBase();
}
static IBibbleOut<Descendant> GetOutDescendant()
{
return null;
}
static IBibbleIn<Base> GetInBase()
{
return null;
}
}
}
Bu işaretler olmadan aşağıdakiler derlenebilir:
public List<Descendant> GetDescendants() ...
List<Base> bases = GetDescendants();
bases.Add(new Base()); <-- uh-oh, we try to add a Base to a Descendant
veya bu:
public List<Base> GetBases() ...
List<Descendant> descendants = GetBases(); <-- uh-oh, we try to treat all Bases
as Descendants