Czy można utworzyć delegata metody instancji bez określania instancji w czasie tworzenia? Innymi słowy, czy możesz utworzyć "statycznego" delegata, który bierze jako swój pierwszy parametr instancję, do której powinna zostać wywołana metoda?"Uncurrying" metoda instancji w .NET
Na przykład, w jaki sposób mogę skonstruować następujące delegate za pomocą refleksji?
Func<int, string> = i=>i.ToString();
Jestem świadomy faktu, że mogę wykorzystać methodInfo.Invoke, ale jest wolniejsza i nie sprawdzić poprawność typu, dopóki nie zostanie wywołana.
Kiedy masz MethodInfo
konkretnego statycznej metodzie możliwe jest skonstruowanie delegata korzystając Delegate.CreateDelegate(delegateType, methodInfo)
, a wszystkie parametry statyczne metody pozostają wolne.
Jak zauważył Jon Skeet, można po prostu zastosować to samo, aby utworzyć otwartego delegata metody instancji, jeśli ta metoda nie jest wirtualna dla typu odniesienia. Wybór metody wywołania metody wirtualnej jest trudny, więc nie jest to tak banalne, a typy wartości wyglądają tak, jakby w ogóle nie działały.
Dla typów wartości, CreateDelegate
eksponaty naprawdę dziwne zachowanie:
var func37 = (Func<CultureInfo,string>)(37.ToString);
var toStringMethod = typeof(int).GetMethod("ToString", BindingFlags.Instance | BindingFlags.Public, null, new Type[] {typeof(CultureInfo) }, null);
var func42 = (Func<CultureInfo,string>)Delegate.CreateDelegate(typeof(Func<CultureInfo,string>), 42, toStringMethod,true);
Console.WriteLine(object.ReferenceEquals(func37.Method,func42.Method)); //true
Console.WriteLine(func37.Target);//37
Console.WriteLine(func42.Target);//42
Console.WriteLine(func37(CultureInfo.InvariantCulture));//37
Console.WriteLine(func42(CultureInfo.InvariantCulture));//-201040128... WTF?
Wywołanie CreateDelegate
z null
jako obiekt docelowy rzuca wiążącej wyjątek jeśli metoda wystąpienie należało do typu wartości (to działa dla typów referencyjnych).
Niektóre follow-up lat później: nieprawidłowo związana cel, który spowodował func42(CultureInfo.InvariantCulture);
wrócić "-201040128"
zamiast "42"
w moim przykładzie było uszkodzenia pamięci, który mógł pozwolić na zdalne wykonanie kodu (cve-2010-1898); zostało to naprawione w 2010 roku w aktualizacji zabezpieczeń ms10-060. Obecne frameworki poprawnie drukują 42! To nie ułatwia odpowiedzi na to pytanie, ale wyjaśnia szczególnie dziwne zachowanie w tym przykładzie.
Jest to jeden z tych przypadków, w których staje się jasne, że C# nadal ma miejsce na rozwój jako język funkcjonalny. Traktowanie funkcji jako pierwszorzędnych obywateli nadal nie jest tak płynne, jak byśmy chcieli. Czy jest jakiś sposób na wykorzystanie dynamicznych funkcji w C# 4, aby tego rodzaju rzeczy były łatwiejsze? – LBushkin
@LBushkin: Nie sądzę. W rzeczywistości, dynamiczne pisanie i lambdas nie układają się ze sobą zbyt dobrze - kompilator musi wiedzieć, jaki typ konwertować wyrażenie lambda na czas kompilacji. –
Ja * miałem * testowałem na int.ToString, ale do rzeczywistego użycia przypuszczam, że mógłbym obejść się bez wirtualnych metod - choć nie bez struktur. W każdym razie, dzięki za heads-up, przeoczyłem złożoność metod wirtualnych, a komunikat o błędzie nie jest dokładnie informacyjny ... –