Mam tę klasę
public class Overloaded
{
public void ComplexOverloadResolution(params string[] something)
{
Console.WriteLine("Normal Winner");
}
public void ComplexOverloadResolution<M>(M something)
{
Console.WriteLine("Confused");
}
}
Jeśli nazywam to tak:
var blah = new Overloaded();
blah.ComplexOverloadResolution("Which wins?");
Pisze Normal Winner
do konsoli.
Ale jeśli dodam inną metodę:
public void ComplexOverloadResolution(string something, object somethingElse = null)
{
Console.WriteLine("Added Later");
}
Otrzymuję następujący błąd:
Wywołanie jest niejednoznaczne między następującymi metodami lub właściwościami:> „
Overloaded.ComplexOverloadResolution(params string[])
” i „Overloaded.ComplexOverloadResolution<string>(string)
”
Rozumiem, że dodanie metody może wprowadzić niejednoznaczność wywołania, ale jest to niejednoznaczność między dwiema już istniejącymi metodami (params string[])
i <string>(string)
! Oczywiście żadna z dwóch metod związanych z niejednoznacznością nie jest nowo dodaną metodą, ponieważ pierwsza to params, a druga to rodzajowa.
Czy to błąd? Jaka część specyfikacji mówi, że tak powinno być?
c#
overload-resolution
McKay
źródło
źródło
'Overloaded.ComplexOverloadResolution(string)'
odnosi się do<string>(string)
metody; Myślę, że odnosi się to do(string, object)
metody bez dostarczonego obiektu.Odpowiedzi:
Tak.
Gratulacje, znalazłeś błąd w rozwiązaniu problemu z przeciążeniem. Błąd odtwarza się w C # 4 i 5; nie odtwarza się w wersji „Roslyn” analizatora semantycznego. Poinformowałem zespół testowy C # 5 i mam nadzieję, że uda nam się to zbadać i rozwiązać przed ostatecznym wydaniem. (Jak zawsze, żadnych obietnic.)
Następuje prawidłowa analiza. Kandydaci to:
Kandydat zerowy oczywiście nie
string
ma zastosowania, ponieważ nie można go zamienić nastring[]
. To pozostawia trzy.Z tych trzech musimy określić jedyną najlepszą metodę. Robimy to, dokonując porównań parami trzech pozostałych kandydatów. Są trzy takie pary. Wszystkie z nich mają identyczne listy parametrów po usunięciu pominiętych parametrów opcjonalnych, co oznacza, że musimy przejść do zaawansowanej rundy rozstrzygającej, opisanej w sekcji 7.5.3.2 specyfikacji.
Co jest lepsze, 1 czy 2? Istotną kwestią rozstrzygającą jest to, że metoda ogólna jest zawsze gorsza niż metoda nieogólna. 2 jest gorsze niż 1. Zatem 2 nie może być zwycięzcą.
Co jest lepsze, 1 czy 3? Istotna rozstrzygająca kwestia to: metoda stosowana tylko w rozszerzonej formie jest zawsze gorsza niż metoda stosowana w jej normalnej formie. Zatem 1 jest gorszy niż 3. Zatem 1 nie może być zwycięzcą.
Co jest lepsze, 2 czy 3? Istotną kwestią rozstrzygającą jest to, że metoda ogólna jest zawsze gorsza niż metoda nieogólna. 2 jest gorsze niż 3. Zatem 2 nie może być zwycięzcą.
Aby zostać wybranym spośród wielu odpowiednich kandydatów, kandydat musi (1) być niepokonany, (2) pokonać co najmniej jednego innego kandydata oraz (3) być jedynym kandydatem, który ma dwie pierwsze właściwości. Kandydat trzeci nie został pokonany przez żadnego innego kandydata i pokonuje co najmniej jednego innego kandydata; jest to jedyny kandydat z tą własnością. Dlatego kandydat trzeci jest jedynym najlepszym kandydatem . Powinien wygrać.
Kompilator C # 4 nie tylko popełnia błąd, jak słusznie zauważyłeś, zgłasza dziwny komunikat o błędzie. To, że kompilator źle analizuje rozdzielczość przeciążenia, jest nieco zaskakujące. To, że wyświetla nieprawidłowy komunikat o błędzie, nie jest zaskakujące; heurystyka błędu „niejednoznacznej metody” zasadniczo wybiera dowolne dwie metody ze zbioru kandydatów, jeśli nie można określić najlepszej metody. Niezbyt dobrze jest znaleźć „prawdziwą” dwuznaczność, jeśli w rzeczywistości taka istnieje.
Można rozsądnie zapytać, dlaczego tak jest. Trudno jest znaleźć dwie metody, które są „jednoznacznie niejednoznaczne”, ponieważ relacja „lepszości” jest nieprzechodnia . Można wymyślić sytuacje, w których kandydat 1 jest lepszy od 2, 2 jest lepszy od 3, a 3 jest lepszy od 1. W takich sytuacjach nie możemy zrobić nic lepszego niż wybranie dwóch z nich jako „niejednoznacznych”.
Chciałbym ulepszyć tę heurystykę dla Roslyn, ale ma ona niski priorytet.
(Ćwiczenie do czytelnika: „Opracowanie algorytmu czasu liniowego w celu zidentyfikowania unikalnego najlepszego członka zbioru n elementów, w którym relacja lepszości jest nieprzechodnia” było jednym z pytań, które zadano mi w dniu, w którym przeprowadzałem wywiad dla tego zespołu. bardzo trudny algorytm; daj mu szansę.)
Jednym z powodów, dla których odkładaliśmy dodawanie opcjonalnych argumentów do C # przez tak długi czas, była liczba złożonych niejednoznacznych sytuacji, które wprowadza do algorytmu rozwiązywania przeciążeń; najwyraźniej nie zrobiliśmy tego dobrze.
Jeśli chcesz zgłosić problem dotyczący połączenia, aby go śledzić, nie krępuj się. Jeśli chcesz tylko zwrócić na to naszą uwagę, pomyśl, że to zrobione. W przyszłym roku przejdę do testów.
Dziękuję za zwrócenie mi na to uwagi. Przepraszamy za błąd.
źródło
Sekcja 7.5.3 (rozpoznawanie przeciążenia) wraz z sekcjami 7.4 (wyszukiwanie elementów) i 7.5.2 (wnioskowanie o typie).
Zwróć szczególną uwagę na sekcję 7.5.3.2 (lepsza składowa funkcji), w której po części stwierdza się, że „parametry opcjonalne bez odpowiadających im argumentów są usuwane z listy parametrów” oraz „Jeśli M (p) jest metodą inną niż generyczna amd M (q) jest metoda ogólna, wtedy M (p) jest lepsze niż M (q). "Jednak nie rozumiem tych części specyfikacji wystarczająco dokładnie, aby wiedzieć, które części specyfikacji kontrolują to zachowanie, nie mówiąc już o ocenie, czy jest zgodne.
źródło
możesz uniknąć tej niejednoznaczności zmieniając nazwę pierwszego parametru w niektórych metodach i określając parametr, który chcesz przypisać
lubię to :
źródło
Jeśli usuniesz
params
z pierwszej metody, to się nie stanie. Pierwsza i trzecia metoda mają oba prawidłowe wywołaniaComplexOverloadResolution(string)
, ale jeśli jest pierwsza metoda,public void ComplexOverloadResolution(string[] something)
nie będzie żadnych niejasności.Podanie wartości dla parametru
object somethingElse = null
sprawia, że jest to parametr opcjonalny, a zatem nie trzeba go określać podczas wywoływania tego przeciążenia.Edycja: kompilator robi tutaj szalone rzeczy. Jeśli przeniesiesz trzecią metodę w kodzie po pierwszej, zostanie ona prawidłowo zgłoszona. Wygląda więc na to, że bierze pierwsze dwa przeciążenia i zgłasza je, bez sprawdzania właściwego.
Edit2: nowe odkrycie. Usunięcie dowolnej metody z powyższych trzech nie spowoduje niejednoznaczności między nimi. Wygląda więc na to, że konflikt pojawia się tylko wtedy, gdy istnieją trzy metody, niezależnie od kolejności.
źródło
Jeśli piszesz
lub po prostu napisz
skończy się na tej samej metodzie , w metodzie
Jest to
params
słowo kluczowe przyczyna, które sprawia, że jest ono najlepiej dopasowane również w przypadku, gdy nie określono parametruJeśli spróbujesz dodać nową metodę, taką jak ta
Doskonale skompiluje i wywoła tę metodę, ponieważ idealnie pasuje do twojego wywołania z
string
parametrem. Wtedy dużo silniejszyparams string[] something
.Deklarujesz drugą metodę, tak jak to zrobiłeś
Kompilator, przeskakuje w całkowitym zamieszaniu między pierwszą metodą a tą, właśnie dodał jedną. Ponieważ nie wie, jaką funkcję powinien teraz wykonać w Twoim telefonie
W rzeczywistości, jeśli usuniesz parametr ciągu z wywołania, takiego jak następujący kod, wszystko kompiluje się poprawnie i działa jak wcześniej
źródło
state
z trzech funkcji, a nie jednej lub kilku z nich, aby być dokładnym. W rzeczywistości wystarczy skomentować którekolwiek z nich, aby rozwiązać problem. Najlepszym dopasowaniem spośród dostępnych funkcji jest ta zparams
, druga to ta zgenerics
parametrem, kiedy dodamy trzecią, powoduje to zamieszanie w jednejset
z funkcji. Myślę, że najprawdopodobniej nie jest to jasny komunikat o błędzie generowany przez kompilator.