Jednym z głównych kłopot z delegata kontrawariancji jest to, że podczas gdy delegat typu np Action<Fruit>
może zostać przekazany do procedury oczekującej na Action<Banana>
, próba połączenia dwóch delegatów, których rzeczywiste typy to Action<Fruit>
, a Action<Banana>
nie powiedzie się *, nawet jeśli obaj delegaci mają typ "kompilacji" Action<Banana>
. Aby obejść ten problem, chciałbym zaproponować stosując sposób podobny do poniższego:
static T As<T>(this Delegate del) where T : class
{
if (del == null || del.GetType() == typeof(T)) return (T)(Object)del;
Delegate[] invList = ((Delegate)(Object)del).GetInvocationList();
for (int i = 0; i < invList.Length; i++)
if (invList[i].GetType() != typeof(T))
invList[i] = Delegate.CreateDelegate(typeof(T), invList[i].Target, invList[i].Method);
return (T)(Object)Delegate.Combine(invList);
}
Given delegata i typ delegata, metoda ta sprawdza, czy typ przeszedł w delegata dokładnie pasuje do określonego typu; jeśli nie, ale metody (y) w oryginalnym delegacie mają odpowiednie podpisy dla określonego typu, nowy delegat zostanie utworzony z niezbędnymi cechami. Zauważ, że jeśli przy dwóch różnych okazjach przekazywana jest ta funkcja, delegaci, którzy nie są odpowiedniego typu, ale których porównywanie są sobie równi, delegaci zwróceni przez tę metodę również będą porównywać siebie nawzajem. Zatem jeśli ma się zdarzenie, które ma przyjąć delegata typu Action<string>
, można zastosować powyższą metodę do konwersji np. przekazany Action<object>
do "rzeczywistego" Action<string>
przed dodaniem lub usunięciem ze zdarzenia.
Jeśli jeden będzie dodanie lub odjęcie przekazywana w pełnomocnik z pola właściwego typu delegatm rodzaju wnioskowanie zachowanie IntelliSense można poprawić, jeśli korzysta się z następujących metod:
static void AppendTo<T>(this Delegate newDel, ref T baseDel) where T : class
{
newDel = (Delegate)(Object)newDel.As<T>();
T oldBaseDel, newBaseDel;
do
{
oldBaseDel = baseDel;
newBaseDel = (T)(Object)Delegate.Combine((Delegate)(object)oldBaseDel, newDel);
} while (System.Threading.Interlocked.CompareExchange(ref baseDel, newBaseDel, oldBaseDel) != oldBaseDel);
}
static void SubtractFrom<T>(this Delegate newDel, ref T baseDel) where T : class
{
newDel = (Delegate)(Object)newDel.As<T>();
T oldBaseDel, newBaseDel;
do
{
oldBaseDel = baseDel;
newBaseDel = (T)(Object)Delegate.Remove((Delegate)(object)oldBaseDel, newDel);
} while (System.Threading.Interlocked.CompareExchange(ref baseDel, newBaseDel, oldBaseDel) != oldBaseDel);
}
Metody te pojawiają się jako metody rozszerzenia dla typów pochodnych od Delegate
i umożliwiają dodawanie instancji takich typów lub odejmowanie od zmiennych lub pól odpowiednich typów delegatów; takie dodawanie lub odejmowanie będzie wykonywane w sposób bezpieczny dla wątków, więc może być możliwe użycie tych metod w metodach dodawania/usuwania zdarzeń bez dodatkowego blokowania.
Zobacz http://stackoverflow.com/questions/129453/net-eventhandlers-generic-or-no –
Myślę, że nie można mieć jednego zdarzenia do obsługi wielu typów, można mieć tylko delegatów. Możesz zmienić program obsługi, aby zaakceptował 'ISomeInterface', a następnie sprawdź typ obiektu, który wysłał. Lub użyj samego interfejsu. http://msdn.microsoft.com/en-us/library/dd469484.aspx – BrunoLM