2010-09-17 11 views
5

Il mio delegato non sembra accettare una sottoclasse, penso che un esempio sia il più semplice.Il delegato non accetta la sottoclasse?

public class A 
{ 
    public A() { } 
} 

public class B : A 
{ 
    public B() { } 
} 

public class Program 
{ 
    private delegate void CallBack(A a); 
    private static CallBack callBack = new CallBack(Test); 

    public Main(string[] args) 
    { 
      callBack(new B()); 
    } 

    private static void Test(A a) 
    { 
      Console.WriteLine("Test()");  
    } 

    // Compilation error occurs if Test becomes: 
    private static void Test(B a) 
    { 
      Console.WriteLine("Test()"); 
    } 
} 

Quando cambio prova ad accettare B getta un errore di compilazione. Non è strano perché B estende A?

compilatore errore:

Nessun sovraccarico per partite di prova richiamata

C'è un modo per rendere un mio delegato accetta una classe che estende A?

+0

vuol gettare un'eccezione o fallisce nella compilation? – Elisha

+1

Questo non dovrebbe causare problemi. Puoi incollare qui il codice modificato? Solo per sapere come lo stai cambiando? –

+0

@Mamta Dalal - Modificata la mia domanda, spero che questo chiarisca un po 'di più. – Kevin

risposta

3

Non è strano perché se si dispone di un oggetto della classe C che si estende A, non avrebbe senso passare a Test() se si accetta solo un B. Qualsiasi metodo utilizzato per un Callback deve accettare qualsiasiA, non solo una sottoclasse specifica. È necessario modificare la firma del delegato Callback per accettare B se si desidera che Test() accetti anche B.

class C : A {}; 

Callback callback = Test; 

callback(new C()); //what if Test() accepted B??? 
+0

Quindi la mia unica opzione sarebbe quella di inserire 'A' in' B' all'interno di 'Test'? – Kevin

+0

@ Kevin: No, non è l'unica opzione. Un'opzione migliore è dichiarare il delegato come "void CallBack (B b)". Puoi ancora usare il metodo 'Test (A a)' allora. – Timwi

+0

@Timwi - Non posso farlo, ho paura. Ho un evento di classe che tutti possono estendere e passare al delegato. – Kevin

-1

C# delegati support both covariance and contravariance, quindi questo dovrebbe funzionare.

Il problema è il sovraccarico.

// this delegate supports contravariance - and subclass of A should work 
delegate void CallBack(A a); 

// however this can't pick up either Test because both could be used 
static CallBack callBack = new CallBack(Test); 

Quale sovraccaricare firma del metodo (o Test(A a)Test(B b)) deve essere risolto al momento della compilazione - tuttavia entrambe potrebbero applicare, quindi viene generato un errore.

Si potrebbe evitare questo, dividendo il sovraccarico:

static void TestA(A a) 
{ 
     Console.WriteLine("Test(a)");  
} 

// Compilation error occurs if Test becomes: 
static void TestB(B a) 
{ 
     Console.WriteLine("Test(b)"); 
} 

// this is valid because it's an exact match 
static CallBack callBackA = new CallBack(TestA); 

// this is valid because delegates support contravariance 
static CallBack callBackB = new CallBack(TestB); 

In entrambi i casi è possibile passare un B:

// B is subclass of A, so can be passed to TestA 
callBackA(new B()); 

// CallBack supports contravariance, so can call TestB 
callBackB(new B()); 

Dato che avete questa controvarianza, perché avete bisogno i sovraccarichi ?

+0

Sto provando a ricreare il modello eventlistener di Flash in C# :) - Grazie per la risposta! Il problema più grande è che gli utenti devono essere in grado di estendere la mia classe 'Event' e passare la loro classe al delegato. La contrravarianza – Kevin

+0

funziona solo quando l'argomento è ** di tipo ** meno derivato di quello nella dichiarazione dei delegati. Non puoi mai assegnare un metodo accettando un argomento 'B' al delegato che hai dichiarato. D'altra parte, se il delegato è stato dichiarato di prendere un argomento 'B', sarebbe possibile aggiungere un metodo di argomento' A' –

1

È abbastanza facile da capire.Ora abbiamo:

class A { } 
class B : A { } 

Scenario 1 all'inizio

public delegate void CallBack(A a); 
public void Test(A a) { } 
CallBack cb = new CallBack(Test); 
cb(new A()); //good and easy usage 

Scenario 2CallBack(A a) e Test(B b)

//compile error, because Test(B b) has a smaller argument scope than CallBack 
//CallBack cb = new CallBack(Test); 

Scenario 3CallBack(B b) e Test(A a)

CallBack cb = new CallBack(Test); 
cb(new A()); //no error, becasue B can convert to A 
+0

Grazie per averlo chiarito. Credo che dovrò lanciare 'A' a' B', quindi, peccato ... – Kevin

8

Non è questo strano perché B estende A?

Hai l'idea giusta, ma nella direzione sbagliata. Consideriamo un esempio più facile da ragionare:

class Animal {} 
class Reptile : Animal {} 
class Snake : Reptile {} 
class Mammal : Animal {} 
class Tiger : Mammal {} 
class Giraffe : Mammal {} 
delegate void D(Mammal m); 
static void DoAnimal(Animal a) {} 
static void DoMammal(Mammal m) {} 
static void DoTiger(Tiger t) {} 

D dm = DoMammal; 
dm(new Tiger()); 

Questo è chiaramente legale. dm deve essere un metodo che accetta un mammifero, e lo è.

D dt = DoTiger; 
dt(new Giraffe()); 

Questo deve essere chiaramente illegale. Non puoi assegnare un metodo che porti una tigre a un delegato che prende un mammifero, perché un delegato che prende un mammifero può prendere qualsiasi mammifero, non solo una tigre. Se questo fosse legale, sarebbe possibile passare una giraffa a un metodo che prende una tigre.

Che dire di questo?

D da = DoAnimal; 
da(new Giraffe()); 

Questo va bene. da è un delegato a un metodo che accetta qualsiasi mammifero. Un metodo che prende chiaramente ogni animale prende anche qualsiasi mammifero. Puoi assegnare DoAnimal (Animale) a un delegato D (Mammifero) perché il Mammifero estende Animale. Vedi ora come hai ottenuto la direzione dell'estensione all'indietro?

tipi restituiti d'altro lavoro manuale il modo di pensare che fanno:

delegate Mammal F(); 
static Animal GetAnimal() {...} 
static Mammal GetMammal() {...} 
static Tiger GetTiger() {...} 

F fm = GetMammal; 
Mammal m = fm(); 

nessun problema.

F ft = GetTiger; 
Mammal t = ft(); 

Nessun problema; GetTiger restituisce una tigre, quindi puoi assegnarla a un delegato che richiede che il suo bersaglio restituisca un mammifero.

F fa = GetAnimal; 
Mammal a = fa(); 

Non va bene. GetAnimal potrebbe restituire un Snake e ora hai una variabile digitata come Mammal che contiene un Snake. Questo deve essere illegale.

Questa funzione è denominata "covarianza e controvarianza delle conversioni dei gruppi di membri" ed è stata introdotta in C# 2.0. Per ulteriori informazioni su questo argomento vedi il mio articolo su di esso:

http://blogs.msdn.com/b/ericlippert/archive/2007/10/19/covariance-and-contravariance-in-c-part-three-member-group-conversion-variance.aspx

Problemi correlati