SORU
26 Kasım 2012, PAZARTESİ


Herhangi bir uyarı veya contravariance belirsizlik neden hata (veya çalışma zamanı hatası)

İlk olarak, bir hatırlayın .NET String IConvertible ICloneable Her ikisi de.

Şimdi, çok basit aşağıdaki kodu göz önünde bulundurun:

//contravariance "in"
interface ICanEat<in T> where T : class
{
  void Eat(T food);
}

class HungryWolf : ICanEat<ICloneable>, ICanEat<IConvertible>
{
  public void Eat(IConvertible convertibleFood)
  {
    Console.WriteLine("This wolf ate your CONVERTIBLE object!");
  }

  public void Eat(ICloneable cloneableFood)
  {
    Console.WriteLine("This wolf ate your CLONEABLE object!");
  }
}

Daha sonra aşağıdaki (bazı yöntem içinde) deneyin:

ICanEat<string> wolf = new HungryWolf();
wolf.Eat("sheep");

Bu derler, bir tane alırhayırderleyici hata ya da uyarı. Çalışırken, yöntemi HungryWolf class benim bildiriminde arayüzü liste sırasına bağlıdır aramış gibi görünüyor. (Virgülle ayrılmış (,) ayrılmış listesinde iki arayüz değiştirmeyi deneyin.)

Soru basit:Bu derleme bir uyarı (veya çalışma zamanında atmak) vermek gerekmez mi?

Muhtemelen bu gibi bir kod ile gelip ilk kişi ben değilim. Kullandımcontravariancearabirimi, ama pek benzer bir örnek yapabilirsinizcovarainacearabirimi. Ve aslında Mr Lippert did just that uzun zaman önce. Onun blog yorum, hemen hemen herkes bir hata olması gerektiğini kabul eder. Henüz bu sessizce sağlar.Neden?

---

Genişletilmiş soru:

String hem Iconvertible (arayüz) ve ICloneable (arayüz) biz istismar yukarıda. Bu iki arabirimi de diğer türemiştir.

Şimdi burada, bir anlamda, biraz daha kötü olduğunu temel sınıflar ile bir örnek.

StackOverflowException SystemException (direct base class) ve Exception (temel sınıf temel sınıf) olduğunu unutmayın. Daha sonra ICanEat<> eskisi gibi (varsa):

class Wolf2 : ICanEat<Exception>, ICanEat<SystemException>  // also try reversing the interface order here
{
  public void Eat(SystemException systemExceptionFood)
  {
    Console.WriteLine("This wolf ate your SYSTEM EXCEPTION object!");
  }

  public void Eat(Exception exceptionFood)
  {
    Console.WriteLine("This wolf ate your EXCEPTION object!");
  }
}

Bunu Test:

static void Main()
{
  var w2 = new Wolf2();
  w2.Eat(new StackOverflowException());          // OK, one overload is more "specific" than the other

  ICanEat<StackOverflowException> w2Soe = w2;    // Contravariance
  w2Soe.Eat(new StackOverflowException());       // Depends on interface order in Wolf2
}

Hala herhangi bir uyarı, hata veya özel durum. Hala arayüzü class beyan sipariş listesi değişir. Ama daha kötü oluyor bence sebebi bu zaman birisi aşırı çözümleme her zaman sadece Exception daha da özel, çünkü SystemException alacağını düşünüyor olabilir.


Bounty önce durumu açıldı: iki kullanıcılardan gelen Üç cevap.

Ödül: geçen gün durumu Hala yeni cevaplar aldı. Eğer cevap kadar gelmezse, Müslüman Ben Dhaou için ödül vermek zorunda kalacağım.

CEVAP
6 Ocak 2013, Pazar


Derleyici uyarı ile VB.NET daha iyi bir şey yok sanırım, ama yine de o kadar ileri gideceğini sanmıyorum. "Doğru olanı" belki de potansiyel olarak faydalı bir şey(kovaryant veya karşıtı iki genel tür parametreleri ile aynı arayüz uygulama) izin vermeme ya da bir dili yeni tanıtımı gerektirir. ne yazık ki,

Bu haliyle, derleyici bir hata şimdi HungryWolf sınıfı dışında atama diye bir yer yok. Bu bir sınıf belirsiz olabilecek bir şey yapmak için iddia ediyor noktasıdır. Gerektiğini söylüyorlar

Ben ** 28 ya da bir şey yemek için nasıl devralmasını uygulanması, belli bir şekilde.

Ve, ben de ** 29, ya da bir şey ya da uygulama miras yemek için biliyorum, belli bir şekilde.

Ancakhiçbir zaman ne yapması gerektiğini belirtireğer plaka üzerinde bir şey alırsahem bir ICloneable IConvertible. Bu ise kesin olarak söylemek için HungryWolf, bir örnek verilirse, derleyici dert değil"Hey, burada ne yapacağımı bilmiyorum!". Ama ICanEat<string> örnek olarak verildiğinde, derleyici keder verecek.Derleyici bu değişken nesnenin gerçek türü nedir, sadece kesinlikle ICanEat<string> uygulamak hiçbir fikrim var.

HungryWolf bir değişken içinde saklanır ne yazık ki, ne olduğu belirsiz, iki kez aynı arabirimi kullanır. Yani şüphe yok ki, biz al bir hata aramaya ICanEat<string>.Eat(string), Bu yöntem var ve olabilir son derece geçerli pek çok diğer nesneler olabilir içine yerleştirilmiş ICanEat<string> değişken (batwad bahsedildiği bu onun cevapları).

Derleyici ICanEat<string> değişken HungryWolf bir nesne atama belirsiz şikayetçi olabilir, ancak daha ileri, iki adım olmasına engel olamaz. HungryWolf bir diğer yöntem içine etrafında geçti olabilir ICanEat<IConvertible> bir değişkene atanmış ve sonunda ICanEat<string> bir değişkene atanabilir.Bunların her ikisi de tamamen yasal atamalarıve derleyici ya da bir şikayet için imkansız olurdu.

BöyleceBirinci seçenekICanEat<IConvertible> ICanEatICanEat<ICloneable>'In genel tür parametre bu iki arabirim birleştirmek olabilir çünkü karşıtı. her iki uygulama HungryWolf sınıfı vermemek için. Ancak, bubir kod için yeteneği yararlı kaldırıralternatif bir çözüm ile.

İkinci seçenekmaalesefderleyici için bir değişiklik yapılması gerekirIL ve CLR. HungryWolf sınıf iki arabirim uygulamak için izin verecek, ama aynı zamanda genel tür parametresi her iki arabirimleri uygulayan arayüzü ICanEat<IConvertible & ICloneable> arayüzü, uygulanması gerekir. Bu büyük olasılıkla en iyi sözdizimi (Eat(T) bu yöntem imzası neye benziyor, Eat(IConvertible & ICloneable food)?) değil. Büyük olasılıkla, daha iyi bir çözüm otomatik olarak oluşturulan sınıf tanımının bir şey olurdu ki, uygulama sınıfı üzerine genel bir tür gibi: bir olacaktır

class HungryWolf:
    ICanEat<ICloneable>, 
    ICanEat<IConvertible>, 
    ICanEat<TGenerated_ICloneable_IConvertible>
        where TGenerated_ICloneable_IConvertible: IConvertible, ICloneable {
    // implementation
}

IL sonra arayüz uygulama şekilleri callvirt bir öğretim için genel sınıflar gibi inşa edilmesine izin edebilmek için değişmiş olurdu:

.class auto ansi nested private beforefieldinit HungryWolf 
    extends 
        [mscorlib]System.Object
    implements 
        class NamespaceOfApp.Program/ICanEat`1<class [mscorlib]System.ICloneable>,
        class NamespaceOfApp.Program/ICanEat`1<class [mscorlib]System.IConvertible>,
        class NamespaceOfApp.Program/ICanEat`1<class ([mscorlib]System.IConvertible, [mscorlib]System.ICloneable>)!TGenerated_ICloneable_IConvertible>

CLR olur sonra süreci callvirt talimatları tarafından inşa arayüzü uygulaması HungryWolf string olarak genel tür parametresi için TGenerated_ICloneable_IConvertible ve kontrol görmek maçlar diğerinden daha iyi arabirim uygulamaları.

Kovaryans için, tüm bu ekstra arabirimleri uygulanması gereken kısıtlamaları ile genel türde parametreler olmak zorunda değil beri daha basit olurduama sadece iki diğer türleri arasında en türev temel türüderleme zamanında bilinen.,

Eğer aynı arayüzü uygulanır katından fazla, sonra sayı fazladan gerekli arayüzler için uygulanan katlanarak büyür, ama bunun maliyeti esnekliği ve tür güvenliği uygulama birden çok karşıtı(veya kovaryant) bir tek sınıf.

Sanmam bu olacak bulunun çerçevesinde, ama benim tercih edilen çözüm, özellikle bu yana yeni bir dil karmaşıklığı ki her zaman kendi kendine yeten bir sınıf olan dilek ne ise şu anda tehlikeli.


düzenleme:
Teşekkür ederim bana bunu hatırlattığın için 71**kovaryans contravariance daha kolaydırortak arayüzleri de dikkate alınması gerektiği gerçeğini nedeniyle. string char[] set halinde en büyük ortak{object, , *ICloneable*62} (63* *olur olurkaplı64**).

Ancak, bu arayüz genel türü için yeni bir sözdizimi parametre kısıtlaması, genel tür parametresi, yalnızca ihtiyacı olduğunu belirtmek gerekir

  • belirtilen sınıf veya bir sınıf belirtilen arabirimler en az bir uygulama devralır
  • belirtilen arabirimler en az birini uygulamak

Muhtemelen gibi bir şey

interface ICanReturn<out T> where T: class {
}

class ReturnStringsOrCharArrays: 
    ICanReturn<string>, 
    ICanReturn<char[]>, 
    ICanReturn<TGenerated_String_ArrayOfChar>
        where TGenerated_String_ArrayOfChar: object|ICloneable|IEnumerable<char> {
}

Genel tür parametre TGenerated_String_ArrayOfChar bu durumda(bir veya daha fazla arabirim ortak) her zaman tedavi olarak object olsa bile, ortak bir temel sınıf var zaten türetilmiş object; çünküsık görülen ortak temel sınıfından miras olmadan ortak bir arayüz uygulayabilir.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Fraser Raft

    Fraser Raft

    9 Mart 2010
  • TheTwistedFrequency

    TheTwistedFr

    26 NİSAN 2010
  • UKF

    UKF

    2 Aralık 2009