Brak ostrzeżenia lub błędu (lub awarii w czasie wykonywania), gdy kontrawariancja prowadzi do niejednoznaczności

Po pierwsze, pamiętaj, że .NETString jest zarównoIConvertible iICloneable.

Rozważmy następujący prosty kod:

//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!");
  }
}

Następnie spróbuj następujących (w niektórych metodach):

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

Kiedy się to kompiluje, dostajemyNie błąd kompilatora lub ostrzeżenie. Po uruchomieniu wygląda na to, że wywołana metoda zależy od kolejności listy interfejsów w moimclass deklaracja dlaHungryWolf. (Spróbuj zamienić dwa interfejsy w przecinku (,) rozdzielona lista.)

Pytanie jest proste:Czy nie powinno to stanowić ostrzeżenia kompilacyjnego (lub rzucania w czasie wykonywania)?

Prawdopodobnie nie jestem pierwszym, który wymyślił taki kod. użyłemkontrawariancja interfejsu, ale możesz zrobić zupełnie analogiczny przykład zCovarainace interfejsu. W rzeczywistościPan Lippert właśnie to zrobił dawno temu. W komentarzach na swoim blogu prawie wszyscy zgadzają się, że powinien to być błąd. Jednak pozwalają na to w milczeniu.Czemu?

---

Pytanie rozszerzone:

Powyżej wykorzystaliśmy toString jest zarównoIconvertible (interfejs) iICloneable (berło). Żaden z tych dwóch interfejsów nie pochodzi od drugiego.

Oto przykład z klasami bazowymi, które w pewnym sensie są nieco gorsze.

Pamiętaj, że aStackOverflowException jest zarówno aSystemException (bezpośrednia klasa bazowa) i anException (klasa bazowa klasy bazowej). A następnie, jeśliICanEat<> jest jak poprzednio):

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!");
  }
}

Przetestuj z:

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
}

Nadal brak ostrzeżenia, błędu lub wyjątku. Nadal zależy od kolejności listy interfejsów wclass deklaracja. Ale powodem, dla którego myślę, że jest gorzej, jest to, że tym razem ktoś może pomyśleć, że rozdzielczość przeciążenia zawsze by się zmieniłaSystemException ponieważ jest bardziej specyficzny niż tylkoException.

Status przed otwarciem nagrody: Trzy odpowiedzi od dwóch użytkowników.

Status ostatniego dnia nagrody: Nadal nie otrzymano nowych odpowiedzi. Jeśli nie pojawią się żadne odpowiedzi, będę musiał przyznać nagrodę muzułmańskiemu Benowi Dhaou.

questionAnswers(4)

yourAnswerToTheQuestion