Czy można w C # zdefiniować taką klasę, że
class GenericCollection<T> : SomeBaseCollection<T> where T : Delegate
Za całe życie nie mogłem tego zrobić wczoraj w .NET 3.5. Próbowałem użyć
delegate, Delegate, Action<T> and Func<T, T>
Wydaje mi się, że powinno to być w jakiś sposób dopuszczalne. Próbuję zaimplementować własną EventQueue.
Skończyło się na tym, że robię to [uwaga na prymitywne przybliżenie].
internal delegate void DWork();
class EventQueue {
private Queue<DWork> eventq;
}
Ale potem tracę możliwość ponownego użycia tej samej definicji dla różnych typów funkcji.
Myśli?
CA1065: Do not raise exceptions in unexpected locations
... Zawsze zakładałem , że powinieneś użyć niestandardowej reguły analizy kodu, aby znaleźć nieprawidłowe zastosowania Twojej klasy, które zwykle nie są dostępne w czasie wykonywania.where T : Delegate
(i ktoś opublikował nową odpowiedź na ten temat poniżej).Tak to jest możliwe w C # 7.3, Ograniczenia rodzina powiększyła się
Enum
,Delegate
aunmanaged
typy. Możesz napisać ten kod bez problemu:void M<D, E, T>(D d, E e, T* t) where D : Delegate where E : Enum where T : unmanaged { }
Z Dokumentów :
Przydatne linki:
Przyszłość języka C # z Microsoft Build 2018
Co nowego w C # 7.3?
źródło
Edycja: w tych artykułach zaproponowano niektóre proponowane obejścia:
http://jacobcarpenters.blogspot.com/2006/06/c-30-and-delegate-conversion.html
http://jacobcarpenters.blogspot.com/2006_11_01_archive.html
Ze specyfikacji C # 2.0 możemy przeczytać (20.7, ograniczenia):
Ograniczenie typu klasy musi spełniać następujące zasady:
I rzeczywiście VS2008 wypluwa błąd:
error CS0702: Constraint cannot be special class 'System.Delegate'
Więcej informacji i dochodzenie w tej sprawie można znaleźć tutaj .
źródło
Jeśli chcesz uzależnić czas kompilacji od IL Weaver, możesz to zrobić za pomocą Fody .
Korzystanie z tego dodatku do Fody https://github.com/Fody/ExtraConstraints
Twój kod może wyglądać tak
public class Sample { public void MethodWithDelegateConstraint<[DelegateConstraint] T> () { } public void MethodWithEnumConstraint<[EnumConstraint] T>() { } }
I być skompilowanym do tego
public class Sample { public void MethodWithDelegateConstraint<T>() where T: Delegate { } public void MethodWithEnumConstraint<T>() where T: struct, Enum { } }
źródło
Delegat obsługuje już tworzenie łańcuchów. Czy to nie spełnia Twoich potrzeb?
public class EventQueueTests { public void Test1() { Action myAction = () => Console.WriteLine("foo"); myAction += () => Console.WriteLine("bar"); myAction(); //foo //bar } public void Test2() { Action<int> myAction = x => Console.WriteLine("foo {0}", x); myAction += x => Console.WriteLine("bar {0}", x); myAction(3); //foo 3 //bar 3 } public void Test3() { Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; }; myFunc += x => { Console.WriteLine("bar {0}", x); return x + 1; }; int y = myFunc(3); Console.WriteLine(y); //foo 3 //bar 3 //4 } public void Test4() { Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; }; Func<int, int> myNextFunc = x => { x = myFunc(x); Console.WriteLine("bar {0}", x); return x + 1; }; int y = myNextFunc(3); Console.WriteLine(y); //foo 3 //bar 5 //6 } }
źródło
Natknąłem się na sytuację, w której musiałem poradzić sobie
Delegate
wewnętrznie, ale chciałem mieć ogólne ograniczenie. W szczególności chciałem dodać procedurę obsługi zdarzeń za pomocą odbicia, ale chciałem użyć ogólnego argumentu dla delegata. Poniższy kod NIE działa, ponieważ „Handler” jest zmienną typu, a kompilator nie będzie rzutowałHandler
naDelegate
:public void AddHandler<Handler>(Control c, string eventName, Handler d) { c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d); }
Możesz jednak przekazać funkcję, która dokona konwersji za Ciebie.
convert
przyjmujeHandler
argument i zwracaDelegate
:public void AddHandler<Handler>(Control c, string eventName, Func<Delegate, Handler> convert, Handler d) { c.GetType().GetEvent(eventName).AddEventHandler(c, convert(d)); }
Teraz kompilator jest szczęśliwy. Wywołanie metody jest łatwe. Na przykład dołączanie do
KeyPress
zdarzenia w kontrolce Windows Forms:AddHandler<KeyEventHandler>(someControl, "KeyPress", (h) => (KeyEventHandler) h, SomeControl_KeyPress);
gdzie
SomeControl_KeyPress
jest cel zdarzenia. Kluczem jest lambda konwertera - nie działa, ale przekonuje kompilator, któremu nadałeś mu prawidłowego delegata.(Rozpocznij 280Z28) @Justin: Dlaczego nie użyć tego?
public void AddHandler<Handler>(Control c, string eventName, Handler d) { c.GetType().GetEvent(eventName).AddEventHandler(c, d as Delegate); }
(Koniec 280Z28)
źródło
Jak wspomniano powyżej, nie można mieć delegatów i wyliczenia jako ogólnego ograniczenia.
System.Object
aSystem.ValueType
także nie może być używane jako ogólne ograniczenie.Aby obejść ten problem, możesz skonstruować odpowiednie wywołanie w swoim IL. Będzie działać dobrze.
Oto dobry przykład autorstwa Jona Skeeta.
http://code.google.com/p/unconstrained-melody/
Swoje odniesienia zaczerpnąłem z książki Jona Skeeta C # in Depth , 3. wydanie.
źródło
Według MSDN
Błąd kompilatora CS0702
Ograniczenie nie może być specjalnym „identyfikatorem” klasy Następujących typów nie można używać jako ograniczeń:
źródło