2012-06-10 9 views
6

jestem początkującym z C# i nie może znaleźć żadnej odpowiedzi na to:Jak przekazać prawidłową akcję <Interface>?

Im próbuje przekazać działania z jakiegoś parametru interfejsu, ale przeforsować funkcji z obiektami, które rozciągają się do tego interfejsu (lub klasy)

// some class extending interface 
public class IntEvent : IFace{} 
public class MoveEvent : IFace{} 

// function i like to use to verify Action 
static void setAction(Action<IFace> evt) { 
    // evt ... 
} 
// function to delegate as Action 
static void evtCheck(IntEvent evt) { 
    // some func 
} 
static void evtMove(MoveEvent evt) { 
    // some func 
} 
// class { 
// call method inside class and delegate this function : 
setAction(evtCheck); 
setAction(evtMove); 

Otrzymuję błąd, że "evtCheck(IntEvent) nie można przekonwertować na Action<IFace>", nawet jeśli IntEvent rozszerza interfejs IFace.

Jak rozwiązać ten problem? Może muszę użyć Func lub Delegata?

Odpowiedz

7

Nie możesz zrobić tego, co próbujesz zrobić - oczekujesz kowariancji, ale Action<T> jest contravariant w swoim jedynym parametrze typu.

Nie można zrobić konwersję metoda grupy od evtCheck do Action<IFace> ponieważ evtCheck potrzebuje dokładniej typ (IntEvent) jako argumentu niż bardziej ogólnym IFace typu że instancja Action<IFace> spodziewa. Jeśli taka konwersja była dozwolona, ​​co by się stało, gdyby delegat został wykonany z argumentem implementującym IFace, ale nie jest to IntEvent?

Myślę, że powinieneś rzucić okiem na swój projekt, ponieważ wygląda na to, że istnieje usterka, ale jeśli chcesz, możesz stworzyć lambdę, aby wymusić odłamek argumentu na pożądany typ i zaakceptować możliwość InvalidCastException:

setAction(iFace => evtCheck((IntEvent)iface)); 

bardziej prawdopodobne, może chcesz zrobić evtCheck przyjąć bardziej ogólny typ lub setAction do przyjęcia bardziej szczegółowych delegata.

4

Action<T> jest kontrawariantny w T, więc metody robienia Action<T> zaakceptuje również Action<U> gdzie T jest pochodną U.

Twoja klasa IntEvent jednak implementuje interfejs IFace, co oznacza, że ​​jeśli kompilator przyjął swoją setAction() zadzwonić, możliwe byłoby wywołanie evtCheck() z argumentem, który jest inny IFace-pochodny i nie IntEvent, błąd w czasie wykonywania i wyraźne naruszenie bezpieczeństwa typu.

obowiązkowe Eric Lippert referencyjny: Covariance and contravariance

EDIT: z opisu wydaje mi się, że to, co naprawdę chcesz to zróżnicowanie w zależności od rodzaju parametr metody, więc na przykład potrzebujesz osobnej akcji dla IntEvent, innej dla (hipotetycznej) DoubleEvent itd. Jeśli tak jest, to faktycznie jesteś po podwójnej wirtualnej wysyłce, która nie jest bezpośrednio obsługiwana w C#, ale możesz ją zasymulować za pomocą wzoru projektowego Visitor .

+0

Tak, próbuję to zrobić. Funkcje działania zawsze oczekują obiektu, który rozszerza ten sam interfejs lub obiekt. Ok, popatrzę na wzór projektu odwiedzającego. – turbosqel