2013-03-26 20 views
8

Jestem zdezorientowany przez problem, który mamy w naszym projekcie. Próbowałem uprościć go odtworzyć efekt:C# Metoda przeciążania i ogólny interfejs

interface IBar { } 

class Bar : IBar {} 

interface IFoo<T> where T : IBar { } 

class Foo<T> : IFoo<T> where T : IBar { } 


class Class1 
{ 
    public void DoTheFoo<T>(T bar) where T : IBar 
    {} 

    public void DoTheFoo<T>(IFoo<T> foo) where T : IBar 
    {} 


    public void Test() 
    { 
     var bar = new Bar(); 
     var foo = new Foo<Bar>(); 

     DoTheFoo(bar); // works 

     DoTheFoo<Bar>(foo); // works 
     DoTheFoo((IFoo<Bar>)foo); // works 
     DoTheFoo(foo); // complains 
    } 
} 

Dla mnie to wygląda dobrze, ale kompilator narzeka na ostatniej rozmowy, ponieważ próbuje DoTheFoo<T>(T bar) zamiast DoTheFoo<T>(IFoo<T> foo) i skarży się, że rodzaj argumentu nie pasuje .

  • Po usunięciu metody DoTheFoo<T>(T bar), ostatnie połączenie działa!
  • Kiedy go zmienić na DoTheFoo<T>(Foo<T> foo), to działa, ale nie mogę użyć tego

To nie jest zbyt trudne, aby obejść ten problem w naszym aktualnym kodzie. Ale jest to a) dziwne i b) szkoda, że ​​nie możemy mieć tych dwóch przeciążonych metod.

Czy istnieje wspólna reguła wyjaśniająca to zachowanie? Czy to możliwe, aby działało (z wyjątkiem podawania różnych nazw)?

+0

Nie sądzę, że jest to duplikat. Istnieje problem, że metody różnią się w ogólnych ograniczeniach. Moje metody różnią się w zadeklarowanym typie argumentu. –

+0

Nie, to duplikat. Przeczytaj uważnie moją odpowiedź. Wyjaśnia, dlaczego rozdzielczość przeciążania wybiera metodę, którą robi w twoim przypadku. –

Odpowiedz

7

To tylko kwestia typu wnioskowania, które nie działają na twoją korzyść w połączeniu z rozdzielczością przeciążenia. Jest to łatwe do ustalenia przez właśnie określająca typ argumentu wyraźnie - nie jest wymagana żadna obsada:

DoTheFoo<Bar>(foo); 

Zazwyczaj jestem nerwowy przeciążeń, które biorą dość różne typy parametrów chociaż. Często kod kończy się prostsze, jeśli po prostu podać metody różne nazwy. Pomijając wszystko inne, wtedy twoje czytelnicy nie trzeba starać się wykonywać uchwały przeciążeniem w tym samym czasie co typ wnioskowania ...

EDIT: Uważam, że problemem jest to, że zamawianie działa tak:

  • Obie metody znajdują
  • Rodzaj wnioskowanie jest stosowany do obu metod bez sprawdzania ograniczeń - tak dla pierwszej metody, otrzymujemy T = Foo<Bar> i drugiej metody otrzymujemy T = Bar. Oba sposoby obowiązują w tym momencie.
  • Przeprowadzana jest rozdzielczość przeciążania, która decyduje, że pierwsza metoda jest najbardziej szczegółowa.
  • Tylko po rozdzielczości przeciążenie została wykonana jest ograniczeniem T sprawdzone - i to nie dlatego, że nie ma żadnego odniesienia do konwersji z Bar do IFoo.

Jest takie Eric Lippert blog post about why the language is designed this way, A blog post I wrote about it i an article I wrote about overloading in general. Każda z nich może lub nie może pomóc :)

EDIT: Pozostawianie typu wnioskowanie na chwilę na bok, powodem pierwsza metoda jest bardziej szczegółowy jest to, że w jednym przypadku mamy do konwersji z Foo<Bar> do Foo<Bar>, aw drugiej przeliczamy z Foo<Bar> na IFoo<Bar>. Zgodnie z częścią 7.5.3.3 specyfikacji C# 5:

względu niejawna C1 konwersji, który konwertuje z wyrażenia E do typu T1 i pośrednio C2 konwersji, który konwertuje z wyrażenia E do typu T2 C1 lepszą konwersję od C2, gdy co najmniej jeden z poniższych warunków: - E ma typ S i konwersji identyczność istnieje od S, T1, ale nie od T2 S - ...

konwersja tożsamość wynosi od typ do siebie, który jest przypadku pierwszego przeciążenia, ale nie jest f lub drugi. Tak więc pierwsza konwersja jest lepsza.

+1

Nie jestem pewien, czy jest to spowodowane wnioskiem typu. Zastanów się, że nie narzeka, że ​​argumenty nie dają odpowiedzi na typ. Działa również, gdy pierwsza metoda DoTheFoo zostanie usunięta ... –

+1

@StefanSteinegger: Wnioskowanie nie działa * w obliczu przeciążenia *. Cała sprawa jest naprawdę podchwytliwa - spróbuj podążyć za nią w specyfikacji - ale wyraźnie typowanie wnioskowania jest * częścią * problemu, w przeciwnym razie nie działałoby, gdy wyraźnie określisz argument typu. –

+0

To w rzeczywistości najdziwniejsza część tej sprawy. Kiedy określam ogólny argument, działa. Chociaż jest w stanie go znaleźć, przynajmniej po usunięciu pierwszego przeciążenia. –

Powiązane problemy