2009-03-28 19 views
25

Piszę .NET On-the-Fly kompilatora dla CLR skryptów i chcą uczynić metoda rodzajowa dopuszczalne wykonanie:Operator jak i klas generycznych

object Execute() 
{ 
    return type.InvokeMember(..); 
} 

T Execute<T>() 
{ 
    return Execute() as T; /* doesn't work: 
    The type parameter 'T' cannot be used with the 'as' operator because 
    it does not have a class type constraint nor a 'class' constraint */ 

    // also neither typeof(T) not T.GetType(), so on are possible 

    return (T) Execute(); // ok 
} 

Ale myślę, że operator as będzie bardzo przydatny: jeśli typ wynik nie jest T metoda zwróci null, zamiast wyjątku! Czy można to zrobić?

Odpowiedz

49

Trzeba dodać

where T : class 

do deklaracji metody, na przykład

T Execute<T>() where T : class 
{ 

Nawiasem mówiąc, jako sugestię, to ogólne opakowanie nie dodaje wiele wartości. Dzwoniący może napisać:

MyClass c = whatever.Execute() as MyClass; 

Albo czy chcą rzucać się na nie:

MyClass c = (MyClass)whatever.Execute(); 

Ogólny sposób wrapper wygląda następująco:

MyClass c = whatever.Execute<MyClass>(); 

Wszystkie trzy wersje mają dokładnie określić te same trzy podmioty, tylko w różnych porządkach, więc żadne nie są prostsze ani wygodniejsze, a jednak ogólna wersja ukrywa to, co się dzieje, podczas gdy "surowe" wersje dają jasno do zrozumienia, czy niech będzie rzut lub null.

(Może to być nieistotne dla Ciebie, jeśli Twój przykład jest uproszczony na podstawie rzeczywistego kodu).

+0

Dziękuję bardzo za odpowiedź. Będę go używał, sprawdzam i oznaczam jako odpowiedź. I mam następne użycie mojego kodu: MyClass c = compiler.Execute (); Myślę, że lepiej niż MyClass c = compiler.Execute() jako MyClass; (sprawdź wewnątrz jest lepiej niż na zewnątrz, tak myślę) – abatishchev

+0

Ale czek jest nadal potrzebny na zewnątrz - czek na zero! :) Dzięki temu, że użytkownik pisze "jako MyClass", stajesz się bardziej jasny, że wymagane jest sprawdzenie zerowej wartości. –

+0

Hm ... Wygląda na to, że masz rację! Polecam używać 'normalnego' Execute(), ale dla użytkownika końcowego może być pomocne posiadanie takiego "nienormalnego" Execute(), dodatkowo "ogólny eksperyment" w jakiś sposób :) – abatishchev

11

Nie można używać operatora as z typowym typem bez ograniczeń. Ponieważ operator as używa null do reprezentowania, że ​​nie należy do typu, nie można go używać w typach wartości. Jeśli chcesz użyć obj as T, T, będzie mieć jako typ referencyjny.

T Execute<T>() where T : class 
{ 
    return Execute() as T; 
} 
1

Wygląda na to, że po prostu dodajesz metodę przechwytywania do rzutowania na typ, jakiego chce użytkownik, a tym samym tylko narzut na wykonanie. Dla użytkownika, pisanie

int result = Execute<int>(); 

nie różni się zbytnio od

int result = (int)Execute(); 

Można użyć się modyfikatora aby zapisać wynik do zmiennej w zakresie rozmówcy i zwraca logiczną flagi aby stwierdzić, czy udało się:

bool Execute<T>(out T result) where T : class 
{ 
    result = Execute() as T; 
    return result != null; 
} 
1

Czy jest szansa, że ​​funkcja Execute() może zwrócić typ wartości? Jeśli tak, to potrzebujesz metody Earwicker dla typów klas i innej ogólnej metody dla typów wartości.Może wyglądać następująco:

Nullable<T> ExecuteForValueType<T> where T : struct 

Logika wewnątrz tej metody twierdzą

object rawResult = Execute(); 

Następnie trzeba by dostać rodzaj rawResult i sprawdzić, czy to może być przypisany do T:

Nullable<T> finalReturnValue = null; 

Type theType = rawResult.GetType(); 
Type tType = typeof(T); 

if(tType.IsAssignableFrom(theType)) 
{ 
    finalReturnValue = tType;  
} 

return finalReturnValue; 

Na koniec utwórz swój oryginalny komunikat Execute, który T ma (klasa lub typ struktury), i wywołaj odpowiednią implementację.

Uwaga: To z trudnej pamięci. Zrobiłem to około rok temu i prawdopodobnie nie pamiętam wszystkich szczegółów. Mimo to, mam nadzieję, że wskazanie ci w ogólnym kierunku pomaga.