2013-02-07 24 views
8

mam funkcje publiczne tak:kompilator nie wywołanie odpowiedniego przeciążenie rodzajowe kiedy zapadają typu wartości

public static T Get<T>(this Mango m, T defaultValue = default(T)) where T : class 
{ 
    //do something; return something; 
} 

public static T? Get<T>(this Mango m, T? defaultValue = default(T?)) where T : struct 
{ 
    //do something; return something; 
} 

Zasadniczo chcę indywidualnie obsługiwać typy referencyjne oraz zerowalne typów. Kompiluje; dopóki nie wezwę typów wartości. Dla typów odniesienia kompiluje.

mango.Get<string>(); // compiles.. 
mango.Get(""); // compiles.. 

mango.Get<int>(); // The type 'int' must be a reference type in order to use it as 
        // parameter 'T' in the generic type or method Get<T>(Mango, T) 
//also   // The call is ambiguous between the following methods or properties: 
        // Get<int>(Mango, int) and Get<int>(Mango, int?) 

Co prawdziwy dwuznaczność jest tutaj? Kiedy T jest int, czy nie można nazwać przeciążeniem strukturalnym? również:

mango.Get<int>(0); // The type 'int' must be a reference type in order to use it as 
        // parameter 'T' in the generic type or method Get<T>(Mango, T) 

Dlaczego kompilator tylko wykrywanie przeciążenia typu odniesienia? Próbowałem mieć dwa oddzielne przeciążenia:

public static T Get<T>(this Mango m) where T : class 
{ 
    return default(T); 
} 

public static T? Get<T>(this Mango m) where T : struct 
{ 
    return default(T); 
} 

public static T Get<T>(this Mango m, T def) where T : class 
{ 
    return default(T); 
} 

public static T? Get<T>(this Mango m, T? def) where T : struct 
{ 
    return default(T); 
} 

Problem nadal występował. I oczywiście, pierwsze dwie metody, których tutaj nie skompilowałem od czasu przeciążenia, nie działają jedynie na podstawie ograniczeń.

Próbowałem usuwając ograniczony przeciążać class i utrzymanie tylko jednego struct ograniczona, podobnie jak to:

public static T? Get<T>(this Mango m, T? defaultValue = default(T?)) where T : struct 
{ 
    //do something; return something; 
} 

mango.Get<int>(); // voila compiles! 
mango.Get<int>(0); // no problem at all.. 
// but now I can't have mango.Get<string>() for instance :(

jestem tylko pozostaje zmiana nazwy dwie funkcje? Uważam, że właściwe jest posiadanie zunifikowanej nazwy, aby osoba dzwoniąca po prostu nie musiała się martwić o szczegóły implementacji, ale wystarczy zadzwonić pod numer Get dla dowolnego typu.

Aktualizacja: Rozwiązanie Marca nie działa, jeśli muszę uniknąć opcjonalnego parametru.

mango.Get<int>(); // still wouldnt work!! 

Ale to nie magia :(:(

public static bool IsIt<T>(this T? obj) where T : struct 
{ 
    return who knows; 
} 

public static bool IsIt<T>(this T obj) where T : class 
{ 
    return perhaps; 
} 

Wszelkimi sposobami Czekam ten sam błąd kompilatora (według mnie), żeby mnie zdenerwować. Ale nie działa tym razem .

Guid? g = null; 
g.IsIt(); //just fine, and calls the struct constrained overload 
"abcd".IsIt(); //just fine, and calls the class constrained overload 

Więc jeśli rozdzielczość przeciążenie jest przed ograniczeniem sprawdzenie jak mówi Marc, nie powinien dostać ten sam błąd i tym razem? Ale nie. Dlaczego czy tak jest? Co się do cholery dzieje? : x

+1

W połowie postu zamierzałem zasugerować, aby wypróbować tylko wersję struct, ale już to zrobiłeś i kompiluje. _Semems_ to dla mnie prawdziwy błąd i powinien zostać zgłoszony. –

+1

@JohnWillemse: Brak błedu, zobacz odpowiedź Marks. –

+0

@ Daniel Hilgarth nie może być błędem, ale z pewnością, gdyby był obsługiwany, to może być przydatna funkcja! – nawfal

Odpowiedz

5

sprawdzanie ograniczeń odbywa się po rezerwa przeciążania, IIRC; rozdzielczość przeciążania wydaje się preferować pierwszą wersję. Można zmusić go do używania drugiego, choć:

mango.Get<int>((int?)0); 

lub nawet:

mango.Get((int?)0); 

Osobiście pewnie tylko zmienić nazwę, aby uniknąć niejasności.

+0

wróci do Ciebie po kilku testach. – nawfal

+0

@MarcGravell W każdym razie, może jest ... dlaczego potrzebujesz przeciążenia dla wartości innej niż null? Wartość niezerowa może być domyślnie odrzucona do wartości null. A może używając opcjonalnych parametrów zamiast przeciążania? Jeśli nullable int nie ma wartości null, wykonaj pewne czynności, jeśli nie, wykonaj inne czynności i użyj nie-nullable int. –

+1

Interesujące jest, dlaczego preferuje pierwsze przeciążenie 'mango.Get (0)'. Teraz utwórz metody nietypowe, a argument jest obowiązkowy, więc napisz te przeciążenia: 'static void M (int? I) {} 'i' static void M (object o) {} ', następnie wywołanie' M (0) 'może albo wstawić' int' i wywołać przeciążenie 'object', albo zawinąć' int' na typ null i wywołać to przeciążenie. Ale ponieważ każdy 'Nullable ' jest 'Obiektem', a odwrotność nie jest prawdą, przeciążenie _ululowalne jest najbardziej" wyspecjalizowane "i musi być preferowane. Zatem 'M (0)' przechodzi do 'M ((int?) 0)', a nie 'M ((obiekt) 0)'. A co z 'mango.Get (0)'? –

0

Pozwól mi spróbować odpowiedzieć na siebie.

Jak mówi marc, sprawdzanie ograniczenie odbywa się po ustąpieniu przeciążenia, a między

public static T Get<T>(this Mango m, T defaultValue = default(T)) where T : class 
{ 
    //do something; return something; 
} 

i

public static T? Get<T>(this Mango m, T? defaultValue = default(T?)) where T : struct 
{ 
    //do something; return something; 
} 

rozdzielczości przeciążenie preferuje wersję class. Ale to tylko wtedy, gdy kompilator ma możliwość wyboru między dwoma podobnymi przeciążeniami (bez opcjonalnego parametru, w którym to przypadku oba przeciążenia stają się takie same, pomijając ograniczenie). Teraz, gdy stosowane jest ograniczenie, wywołanie kończy się niepowodzeniem dla Get<int>, ponieważ int nie jest class.

Rzeczy zmieniają się nieco po podaniu parametru domyślnego. Jeśli zadzwonię

mango.Get(0); 

Kompilator jest wystarczająco stanie wywołać odpowiednią przeciążenie, ale które teraz akceptuje int przeciążenie lub T where T: struct? Nie ma ani jednego. W podanym przykładzie oczekuje się, że drugim parametrem będzie T?, a nie T. Kompilator automatycznie nie usuwa przeciążenia, stosując wszystkie dostępne rzutowania dla każdego typu argumentu. To nie jest błąd, ale to jedna mniej funkcji, to wszystko. Jeśli mogę to zrobić:

int? i = 0; 
mango.Get(i); 

działa prawo przeciążenie jest tzw. Tak samo dzieje się w drugim przykładzie. Działa, ponieważ dostarczam odpowiedni parametr.

Kiedy zadzwonić:

Guid? g = null; 
g.IsIt(); 

obj jest znany g i stąd T równa Guid. Ale jeśli zadzwonię

Guid g = Guid.NewGuid(); 
g.IsIt(); 

To nie działa, ponieważ g jest teraz Guid i nie Guid? i kompilator robi zrobić casting na bieżąco, a nie trzeba by poinformować kompilator wyraźnie.

jestem ok, z tym, że kompilator nie robi casting automatycznie ponieważ będzie zbyt dużo, aby obliczyć dla każdego możliwego rodzaju, ale to, co wydaje się być niedobór w C# jest fakt, że ograniczenie kontroli nie bierze udziału w rozwiązywaniu przeciążenia. To nawet jeśli podam typ taki jak mango.Get<int>() lub mango.Get<int>(0), rozdzielczość przeciążania nie będzie preferowana dla wersji struct i dla argumentu defaultValue będzie używana metoda default(int?). Wydaje mi się dziwny.

1

Co ciekawe, kompilator sprawdzi ograniczenia określone w typach ogólnych, które są używane w sygnaturze metody, ale nie w przypadku ograniczeń wewnątrz samego podpisu.

Tak więc, jeśli metoda zaakceptowała dwa parametry, jeden z typu T where T : struct wraz z Nullable<T>[], kompilator nie rozważyłby metody dla żadnego T, która nie byłaby strukturą.Określona metoda ograniczenia struct dla T nie jest brana pod uwagę przy ocenie przeciążenia, ale fakt, że Nullable<T> ogranicza się do struktury, to T.

naprawdę znaleźć całkowitą niezdolność do rozważenia ograniczenia w ocenie przeciążeniowym dziwne, biorąc pod uwagę, że można określić wartość domyślną null dla parametru Nullable<T>[], i udawać, że parametr nie istnieje. Kompilatory vb.net i kompilatory C# wydają się jednak różnić, jeśli chodzi o to, co uważają za niejednoznaczne i co akceptują.

+0

'kompilator sprawdzi ograniczenia określone w typach ogólnych, które są używane w sygnaturze metody, ale nie w przypadku ograniczeń wewnątrz samego podpisu." Było bardzo mylące, ale 'Ograniczenie struktury określonej przez metodę dla T nie jest brane pod uwagę przy ocenie przeciążenia, ale fakt, że Nullable ogranicza T do struct, to jest "pomógł :) Czy vb wróży poprawnie przeciążenie w moim przypadku? – nawfal

+0

@nawfal: Nie ma, ale dodanie fałszywego parametru typu zawierającego parametr typu constated o ograniczonej klasie (w ten sam sposób, w jaki "Nullable " zawiera parametr typu strukturalnego) może pomóc; vb.net wciąż dostrzega niejednoznaczność w przypadku dwóch sygnatur, które są identyczne, z wyjątkiem parametru dummy default-null, ale jeśli nie potrzebujesz ogólnego parametru typu w obrębie funkcji, możesz mieć możliwość pobrania parametru wpisz 'Object'. – supercat