2013-02-05 12 views
8

Jak uzyskać wartość argumentu dostarczoną do zamkniętej/skonstruowanej metody ogólnej?Uzyskaj ogólny typ argumentu i wartość dostarczoną do ogólnej metody

Minęło trochę czasu odkąd nie dotknąłem Reflection. Wszystko to znajdowało się z tyłu mojego umm, cokolwiek.

class Program 
{ 
    static void Main(string[] args) 
    { 
     new ConcreteFoo().GenericMethod<int>(5); 
     Console.ReadKey(); 
    } 
} 

class ConcreteFoo 
{ 
    public void GenericMethod<Q>(Q q) 
    { 
     var method = MethodInfo.GetCurrentMethod();  
     var parameters = method.GetParameters();  
     if (parameters.Length > 0) 
      foreach (var p in parameters) 
       Console.WriteLine("Type: {0}", p.ParameterType); 

     // That still prints Q as the type. 
     // I've tried GetGenericArguments as well. No luck.     
     // I want to know: 
     // 1) The closed type, i.e. the actual generic argument supplied by the caller; and 
     // 2) The value of that argument 
    } 

    public void GenericMethodWithNoGenericParameters<Q>() 
    { 
     // Same here 
    } 
} 

class GenericFoo<T> 
{ 
    public void NonGenericMethod(T t) { /* And here*/ } 
    public void GenericMethod<Q>(Q q) { /* And here */ } 
} 

UPDATE

To pytanie jest absurdalne, a tym samym zamknięty przez pytającego. Chce go zatrzymać, żeby pokazać swoim dzieciom, jak głupi tatuś był, jeśli kiedykolwiek okazali się programistami C#.

+3

Czy istnieje szczególny powód, dla którego nie można użyć 'q.GetType()'? –

+5

'typeof (Q)' da ci typ dostarczony w czasie wykonywania dla obecnej metody. jeśli to jest to, o co prosisz ... –

+6

Tylko dla wyjaśnienia różnicy między dwoma powyższymi komentarzami: 'q.GetType()' otrzyma aktualny typ obiektu, co oznacza, że ​​może to być podklasa lub implementacja (jeśli 'Q' był interfejsem). 'typeof (Q)' zwróci ogólny typ używany podczas wywoływania metody (jawnie: 'GenericMethod (1)' ('Q' to' Object') lub niejawnie: 'GenericMethod (1)' ('Q' is 'Int32') niezależnie od typu przekazanego) –

Odpowiedz

8

Krótka odpowiedź to typeof (Q).

Długa odpowiedź (która próbuje wyjaśnić, dlaczego nie można wyliczyć te typy i trzeba zapisać je specjalnie) idzie tak:

Każda metoda rodzajowa (co jest bardziej ogólne niż to deklarowania klasa) ma odpowiednie , odrębne instancje MethodInfo dla wszystkich jego (kiedykolwiek) dotkniętych indywidualizacji i inne MethodInfo dla metody "template"/open.

Można to wykorzystać, aby uzyskać to, co chcesz:

class ConcreteFoo {  
    public void GenericMethod<Q>(Q q) { 
    var method = MethodInfo.GetCurrentMethod(); 
    var closedMethod = method.MakeGenericMethod(typeof(Q)); 

    // etc 
    } 
} 

Dlaczego tak jest? Dzieje się tak, ponieważ żadna z "wyliczających operacji" w odbiciu nie zwraca instancji MethodInfo, które odwołują się do zamkniętych określeń.

Jeśli wyliczenia statyczne metody zadeklarowane przez ConcreteFoo tak:

var atTime1 = typeof(ConcreteFoo).GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); 

ConcreteFoo.GenericMethod(true); 

var atTime2 = typeof(ConcreteFoo).GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); 

dostaniesz identyczne wyniki. Jeśli chodzi o GenericMethod i jego otoczenie poszczególnych pojęć, otrzymasz tylko obiekt odbicia skojarzony z GenericMethod (wariant otwarty).

atTime2 nie będzie zawierać dodatkowej metody MethodInfo odnoszącej się do świeżo wyskoczonego GenericMethod < bool>.

Ale to nie jest takie złe, prawda? Metoda GetMethods() powinna zwracać spójne wyniki, a wyniki nie mogą się różnić w czasie. algebry metod generycznych jest całkiem ładny, jeśli chodzi o to "Nawigacja" operacje:

  1. Wszystkie otwarte MethodInfos mają IsGenericMethod = true i IsGenericMethodDefinition = true
  2. zamknięte MethodInfos mają IsGenericMethod = true i IsGenericMethodDefinition = false
  3. Wywołując metodę .GetGenericMethodDefinition() na zamkniętej metodzie MethodInfo, otrzymujemy otwartą wiadomość:
  4. Poprzez wywołanie.MakeGenericType (params Type [] rodzaje) na otwartym MethodInfo dostać cokolwiek zamkniętego chcesz (bez składniowo świadomy tego, co te typy są iz możliwością odbierania wyjątek za nieprzestrzeganie klauzul gdzie)

to samo dotyczy operacji odbicia, które pochodzą z perspektywy bieżącego wątku (zamiast się od zespołów i typów):

MethodBase MethodInfo.GetCurrentMethod() 

i

StackTrace trace = new StackTrace(); 
IEnumerable<MethodBase> methods = from frame in trace.GetFrames() 
            select frame.GetMethod(); 

nigdy zwracają rzeczywiste zamknięte warianty ogólnych metod (jeśli istnieją) , które faktycznie znajdują się na górze lub na bieżącym stosie wywołań.

W ten sposób Twoje pytanie nie jest absurdalne, ponieważ, podczas gdy w przypadku GetCurrentMethod można łatwo zastąpić go GetCurrentMethod powiększonej MakeGenericMethod plus składniowo dostępnych typeof (cokolwiek), można mówię o twoich rozmówcach.

Tak więc dla metod nietypowych zawsze można spojrzeć na swój stos i dokładnie wiedzieć, jakie są typy parametrów tych metod. Metody, które się nawzajem odwołały, a ostatecznie twoje, zostały wywołane ... Ale w przypadku generycznych (które są naprawdę naprawdę zamknięte, ponieważ powtarzam, nielogiczne jest myślenie o ogólnej metodzie, która uruchamia i wywołuje inną i została wywołana przez kogoś innego (itp.) jest otwarta) nie można znaleźć typów parametrów, tak jak nie można poznać wartości zmiennych lokalnych tych metod (które są deterministyczne, ale byłaby to wielka wada wydajności, która spowodowałaby, że możliwość).

+0

Właśnie widziałem tę odpowiedź właśnie teraz. Nie jestem świetny w utrzymywaniu tabulatora odpowiedzi na moje poprzednie pytania. Bardzo dziękuję za tak rozbudowaną odpowiedź. Przeczytam to jutro i widzę, jak nadchodzi zielony check. :-) –

Powiązane problemy