2013-03-15 24 views
5

Supponiamo di avere due classi.Metodo di estensione Ignora

public class A {...} 
public class B : A {...} 

Quello che voglio è quello di eseguire l'override di una funzione di estensione per entrambi i tipi.

public static void HelperExtension(this A a) {...} 
public static void HelperExtension(this B b) {...} 

So che non sono funzioni virtuali o comportarsi come loro. Tuttavia, in questo caso, mi chiedo il comportamento del compilatore.

C'è un modo per chiamare la funzione per il tipo B senza risolvere il suo tipo? O qualche suggerimento di auto-risoluzione?

+0

L'estensione non deve essere statica? – Alex

+0

Sì, mi mancava:/ – ozanbora

+0

Hai * bisogno * di risolvere il secondo metodo di estensione se si tratta di un oggetto B ma hai un riferimento A ad esso? Dopotutto, chiamare i metodi virtuali sul riferimento A si risolverà comunque sull'oggetto B. Sei tu a controllare le classi A e B? In tal caso, non dovresti comunque utilizzare un metodo di estensione, ma modifica le classi con metodi normali. – nvoigt

risposta

2

Come hai detto, non c'è modo di rendere l'estensione virtuale.

Si potrebbe implement the entire virtual method pattern yourself through static methods ma ho una forte sensazione che non sarà di alcuna utilità pratica per voi, è più di una soluzione teorica interessante in quanto il lavoro coinvolto sarebbe proibitivo per qualcosa di così semplice.

Se ci sono un numero finito fisso di possibili classi secondarie si potrebbe avere il primo metodo avere qualcosa di simile:

public static void HelperExtension(this A a) 
{ 
    B b = a as B; 
    if(b != null) 
     HelperExtension(b); 
    else 
     //the rest of the method. 
} 

È possibile utilizzare un Switch o anche un Dictionary<Type, Action<A>> se si dispone di un sacco di sottoclassi, ma sarebbe noioso, difficile da mantenere e non supportare gli ereditarieri arbitrari non noti al momento della compilazione.

Un'altra opzione è quella di sfruttare sostanzialmente la funzionalità del compilatore in fase di compilazione attraverso l'uso di dynamic. Consiglio caldamente di evitarlo quando possibile, ma in questo caso particolare ti permetterebbe di avere un'unica estensione pubblica su A, una serie di metodi statici privati ​​(con un nome diverso) per ogni sottotipo e quindi una singola chiamata di dispacciamento:

public static void HelperExtension(this A a) 
{ 
    ExtenstionImplementation((dynamic)a); 
} 

private static void ExtenstionImplementation(A a){...} 
private static void ExtenstionImplementation(B a){...} 
private static void ExtenstionImplementation(C a){...} 
5

Questo non è prioritario - è sovraccarico, semmai.

E va bene - dal momento che le firme sono diversi, il compilatore non avrà alcun problema a compilarlo, anche nello stesso spazio dei nomi.

Tuttavia, quale metodo di estensione verrà chiamato dipende dal tipo esatto della variabile.


Ora:

C'è un modo per chiamare la funzione per il tipo B senza risolvere il suo tipo? O qualche suggerimento di auto-risoluzione?

Senza eseguire il cast non è possibile. L'estensione è legata al tipo esatto che viene esteso, quindi è necessario avere il tipo esatto per chiamare un'estensione su di esso.

Questo è il motivo per la maggior parte di LINQ è definito sulla IEnumerable<T>.

+2

Questo non è quello che sta chiedendo. Vuole essere in grado di fare 'A a = new B(); a.HelperExtension(); 'e chiamatelo il secondo metodo invece del primo. – Servy

+0

@Servy - Mancato. Risposta aggiornata – Oded

+0

Mi aspetto un simile commento, il fatto è che se uso il metodo di classe anziché l'estensione questo sarebbe eccessivo. Grazie ancora :) – ozanbora

0

Recentemente ho affrontato un problema simile. Ho una libreria di classi di terze parti contenente una gerarchia di classi (diciamo IBase, Base e Derived, dove IBase è in realtà un'interfaccia).

public interface IBase {...} 
public class Base : IBase {...} 
public class Derived : Base {...} 

Quindi, ho una mia classe che contiene un riferimento ext a IBase. Il tipo concreto di ext può essere Base così come Derivato.

public class MyClass { 
    // other stuff 
    public IBase ext; 
} 

Quello che in realtà serviva era un metodo AddedMethod virtuale() definito all'interno IBase e sovrascritto in ogni classe discendente, ma che non era praticabile. Quindi, si potrebbe essere tentati di definire una classe che contiene un insieme di metodi di estensione di overload:

public static class MyExtensions 
{ 
    public static void AddedMethod(this IBase arg) {...} 
    public static void AddedMethod(this Base arg) {...} 
    public static void AddedMethod(this Derived arg) {...} 
} 

poi, chiamare ext.AddedMethod() su MyClass oggetti. Questo non funziona: poiché i metodi di estensione sono legati staticamente, il primo metodo (ad esempio AddedMethod (questo IBase arg)) viene sempre chiamato, indipendentemente dal tipo effettivo di ext. Il problema può essere aggirato attraverso la definizione di un unico metodo di estensione su IBase, quindi utilizzando riflessione per selezionare la corretta istanza di un metodo statico privato il cui tipo di argomento corrisponde al tipo effettivo passato al metodo di estensione:

public static class MyExtensions 
{ 
    // just one extension method 
    public static void AddedMethod(this IBase arg){ 
     // get actual argument type 
     Type itsType = arg.GetType(); 

     // select the proper inner method 
     MethodInfo mi = typeof(MyExtensions).GetMethod("innerAddedMethod", 
      BindingFlags.NonPublic | BindingFlags.Static, 
      null, 
      new Type[] { itsType }, 
      null); 

     // invoke the selected method 
     if (mi != null) { 
      mi.Invoke(null, new object[] { arg }); 
     } 
    } 

    private static void innerAddedMethod(Base arg) { 
     // code for Base type arg 
    } 

    private static void innerAddedMethod(Derived arg) { 
     // code for Derived type arg 
    } 

Sia una nuova classe derivata Derived2 dovrebbe essere aggiunta alla gerarchia IBase, dovremo semplicemente aggiungere alla classe MyExtensions un innerAddedMethod() sovraccarico che accetta Derived2 come argomento.

Problemi correlati