class Animal
{
public bool IsHungry { get; }
public void Feed() { }
}
class Dog : Animal
{
public void Bark() { }
}
class AnimalGroup : IEnumerable<Animal>
{
public IEnumerator<Animal> GetEnumerator() { throw new NotImplementedException(); }
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
}
class AnimalGroup<T> : AnimalGroup, IEnumerable<T>
where T : Animal
{
public new IEnumerator<T> GetEnumerator() { throw new NotImplementedException(); }
}
Her şey düz bir foreach ... mesela ile harika çalışıyor Aşağıdaki ince derler:
var animals = new AnimalGroup();
var dogs = new AnimalGroup<Dog>();
// feed all the animals
foreach (var animal in animals)
animal.Feed();
// make all the dogs bark
foreach (var dog in dogs)
dog.Bark();
Biz de tüm aç hayvanları beslemek için kod derlemek olabilir:
// feed all the hungry animals
foreach (var animal in animals.Where(a => a.IsHungry))
animal.Feed();
... ama benzer kod değişiklikleri kullanmaya çalışırsanız sadece aç köpekler kabuğunu yapmak biz kullanılabilecek bir uzantısı yöntemi yoktur aslında gibi bu çok garip bir hata gibi görünen bir derleme hatayı
// make all the hungry dogs bark
foreach (var dog in dogs.Where(d => d.IsHungry))
dog.Bark();
// error CS1061: 'AnimalGroup<Dog>' does not contain a definition for 'Where' and
// no extension method 'Where' accepting a first argument of type 'AnimalGroup<Dog>'
// could be found (are you missing a using directive or an assembly reference?)
olsun. Derleyicinin, bunun için hangi genel parametrenin kullanılacağını belirlediği ve belirsiz genel parametreler için en iyi eşleme uzantısı işlevine ilişkin yeterli bir hata mesajının bulunmadığını düşünür.
yerine, ben bir arayüz olmadan AnimalGroup<T>
tanımlayacak: (foreach arabirimi yoktur olsa bile GetEnumerator işlevini kullanır çünkü)
class AnimalGroup<T> : AnimalGroup
where T : Animal
{
public new IEnumerator<T> GetEnumerator() { throw new NotImplementedException(); }
}
ilk 3 test durumları hala çalışır. 4. vakadaki hata mesajı daha sonra bir hayvan yapmaya çalışan bir köpeğe gider (bu bir köpek olur, fakat tip sistem KNOW bir köpekdir). Bu, foreach döngüsünde var
'un Dog
'a değiştirilmesiyle düzeltilebilir (ve tüm köpekler numaralandırıcıdan geri döndürülürse, dog?.Bark()
kullanarak tamlık için). benim gerçek kullanım durumunda
, ben çok daha muhtemeldir AnimalGroup
den AnimalGroup<T>
başa isteyen edilecek (ve aslında IReadOnlyList<T>
yerine IEnumerable<T>
kullanıyor) değilim. 2 ve 4 numaralı durumlarda olduğu gibi kod yapılması beklendiği gibi Linq işlevlerinin doğrudan AnimalGroup
(tamlık için de istenebilir, ancak çok daha düşük öncelikli) olarak adlandırılmasına izin vermekten çok daha yüksek bir önceliktir. Bu yüzden, bir arabirim olmadan AnimalGroup
yeniden tanımlanarak Bu: Bu üçüncü durumda hatayı taşır
class AnimalGroup
{
public IEnumerator<Animal> GetEnumerator() { throw new NotImplementedException(); }
}
class AnimalGroup<T> : AnimalGroup, IEnumerable<T>
where T : Animal
{
public new IEnumerator<T> GetEnumerator() { throw new NotImplementedException(); }
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
}
"tüm aç hayvanları beslemek" - Ben ile yaşayabilir, (ve hata mesajı bu bağlamda mantıklı gerçekten hiçbir geçerli uzatma yöntemi yoktur) şimdi. (Şimdi düşünüyorum, temel sınıfta jenerik olmayan bir IEnumerable
arabirim bırakmış olabilir, ancak bu Linq işlevleri sadece genel arabirimde çalışır, foreach gerektirmez ve IEnumerable kullanarak arayanlar gibi, hiçbir yararı yoktur sonucu object
'dan göndermeliyim.
Ben ikinci 2 doğrudan bir başka yerine (Enumerable.Where
çağırarak beklendiği gibi bu test durumları Tüm 4 derlemek, böylece AnimalGroup
yeniden tanımlamak ve/veya AnimalGroup<T>
ki böyle Henüz düşünce ettik bir yolu var mı Where
Fonksiyonu tanımlarım)?
İlginç bir sınır da var genericAnimals = new AnimalGroup<Animal>;
. Farklı bir tür parametresiyle derleme yapmayan aynı sınıf olsa bile, genericAnimals.Where(...)
'u beklendiği gibi kullanır.Dediğiniz gibi
class AnimalGroup<T> : IEnumerable<T>
where T : Animal
{
public IEnumerator<T> GetEnumerator() { throw new NotImplementedException(); }
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
}
class AnimalGroup : AnimalGroup<Animal> { }
AnimalGroup neden jenerik olmayan AnimalGroup'dan miras alınır? –
pkuderov
@pkuderov - Bunun iyi bir neden olduğunu düşündüm, ama onları değiştirmek hiç sorun yaratmıyor gibi görünüyor, bu yüzden kötü bir sebep olmalı! – Steve