2012-03-08 23 views
5

To część ciekawości, a częściowo dlatego, że próbowałem po prostu użyć tego. Jeśli masz następujące definicje, kompilator nie zezwala na to, ponieważ mówi, że element jest już zdefiniowany. Jakie jest uzasadnienie braku dopuszczalnego wyłączenia ogólnych parametrów typu?Przeciążanie ogólnych parametrów typu niedozwolone?

void Get<T>() where T: struct {} 
void Get<T>() where T: class {} 

Wydaje mi się, że nie ma z tym nieodłącznego problemu. Można argumentować, że nie zawsze jest jasne, który kompilator powinien wybrać w przypadkach, w których definicje nakładają się (ale wspólna rozdzielczość wydaje się być najbardziej specyficznym dopasowaniem).

Czy ktoś może mi pomóc zrozumieć lub wskazać zasób, za którym to uzasadnieniem się nie zgadza?

+0

Ciekawe, jakie rzeczy próbowałaś wykorzystać w rzeczywistości? – AakashM

+0

@AakashM: proste, musiałem konwertować typy zerowane w inny sposób niż nie-zerowalne typy wartości. Myślałem, że ogólne przeciążenie to szybki sposób, aby to naprawić. Metoda przyjmuje obiekt i typ param i nie mogę ograniczyć parametru typu (cóż, mogę, ale nie mogę go przeciążać). – Abel

Odpowiedz

9

Eric Lippert już odpowiedział na to jeden, w blogu na ogólnych ograniczeń i podpisów metoda: http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

ograniczeń na typy generyczne nie są częścią podpisów metoda w CLR, dlatego nie można mieć dwóch metod, które różnią się tylko ogólnymi ograniczeniami typu. Bez wsparcia CLR byłoby dość kłopotliwe, aby C# obsługiwał je w sensowny sposób, kompatybilny z innymi językami .NET.

+0

Jeśli przedstawisz to w ten sposób, wydaje się to logiczne. Można się tylko zastanawiać, dlaczego nie jest częścią sygnatury metody? Czy Anders Hejlsberg miał dobry powód, aby nie projektować go w jakikolwiek inny sposób? Nie przeczytałem jeszcze w pełni artykułu Lipperta. Dzięki za wskaźnik. – Abel

+1

Domyślam się, że jest to ograniczenie CLR - CLR nie uważa również ogólnych ograniczeń typu za część sygnatury metody. Nie ma sposobu na reprezentowanie ogólnych ograniczeń w binarnej reprezentacji sygnatur metod, więc jest to dość głębokie ograniczenie. – thecoop

+4

@Abel: thecoop jest poprawny; jest to ograniczenie CLR. Oczywiście teoretycznie możemy wiązać ograniczenia z podpisem, co z pewnością byłoby pomocne w konkretnym przypadku. Ale w ogólnym przypadku przedstawia wiele problemów.Na przykład: "void M () gdzie T: IFoo {}" i "void M () gdzie T: IBar {}" byłyby różnymi sygnaturami metod; więc jaka jest metoda "M " gdzie 'Blah' jest klasą, która implementuje zarówno' IFoo' i 'IBar'? Zasady dotyczące niejednoznacznych podpisów są już wystarczająco skomplikowane; nie dodawajmy zupełnie nowych sposobów bycia niejednoznacznym. –

1

Ograniczenie struct na Nullable<T> jest IMHO naprawdę niefortunne. Coś takiego jak Nullable<String> lub Nullable<Nullable<Nullable<int>>> może mieć marnotrawstwo, ale co z tego? Box the first jako treść; rozpakuj go jako treść i ustaw wartość HasValue, jeśli zawartość ma wartość inną niż null. Umieść tę pierwszą jako int, jeśli wszystkie wartości zerulców zgłaszają HasValue, a po rozpakowaniu ustaw HasValue wszystkich zagnieżdżonych elementów, jeśli zawartość nie jest pusta.

W przeciwnym razie proponuję utworzenie statycznej klasy ogólnej z parametrem typu T, który zawiera właściwość delegata, która jako parametr przyjmuje wartość T. Właściwość powinna zwrócić zawartość prywatnego pola, które powinno zostać zainicjowane, aby wskazywało metodę sprawdzającą typ T i odpowiednio ustawić delegata na wersję struct lub struct lub struct.

Oto próbka tego, o czym mówię; ten używa różnych ograniczeń interfejsu, a nie ograniczeń strukturalnych/klasowych, ale te same zasady mogą być równie skuteczne.

 
     static class _FooDispatcher<T> 
     { 
      public static Action<T> Foo = setupFoo; 

      static void doFooWithIGoodFoo<TT>(TT param) where TT : IGoodFoo 
      { 
       Console.WriteLine("Dispatching as IGoodFoo with {1} type {0}", typeof(TT).ToString(), typeof(TT).IsValueType ? "value" : "reference"); 
       param.Foo(); 
      } 
      static void doFooWithIOkayFoo<TT>(TT param) where TT : IOkayFoo 
      { 
       Console.WriteLine("Dispatching as IOkayFoo with {1} type {0}", typeof(TT).ToString(), typeof(TT).IsValueType ? "value" : "reference"); 
       param.Foo(); 
      } 
      static void doFooSomehow<TT>(TT param) 
      { 
       Console.WriteLine("Nothing exciting with {1} type {0}", typeof(TT).ToString(), typeof(TT).IsValueType ? "value" : "reference"); 
      } 
      static void setupFoo(T param) 
      { 
       System.Reflection.MethodInfo mi; 
       if (typeof(IGoodFoo).IsAssignableFrom(typeof(T))) 
        mi = typeof(_FooDispatcher<T>).GetMethod("doFooWithIGoodFoo", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); 
       else if (typeof(IOkayFoo).IsAssignableFrom(typeof(T))) 
        mi = typeof(_FooDispatcher<T>).GetMethod("doFooWithIOkayFoo", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); 
       else 
        mi = typeof(_FooDispatcher<T>).GetMethod("doFooSomehow", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); 
       Foo = (Action<T>)(@Delegate.CreateDelegate(typeof(Action<T>), mi.MakeGenericMethod(typeof(T)))); 
       Foo(param); 
      } 
     } 
Powiązane problemy