2010-02-18 7 views
8

Come posso prendere il Type della classe ereditata e passarlo nel costruttore di base della classe anche ereditato? Vedere il codice di esempio:Passare il tipo di oggetto corrente nella chiamata del costruttore di base

// VeryBaseClass is in an external assembly 
public abstract class VeryBaseClass 
{ 
    public VeryBaseClass(string className, MyObject myObject) 
    { 

    } 
} 

// BaseClass and InheritedClass are in my assembly 
public abstract class BaseClass : VeryBaseClass 
{ 
    public BaseClass(MyObject myObject) : 
     base(this.GetType().Name, myObject) // can't reference "this" (Type expected) 
    { 

    } 
} 

public class InheritedClass : BaseClass 
{ 
    public InheritedClass(MyObject myObject) 
    { 

    } 
} 

La linea base(typeof(this).Name, myObject) non funziona perché non posso ancora riferimento this, come l'oggetto non è terminata la costruzione e pertanto non esiste.

È possibile afferrare lo Type dell'oggetto attualmente in costruzione?

EDIT:

Corretto il campione come orsogufo suggested, ma ancora non funziona, come this è indefinito.

EDIT 2:

tanto per chiarire, voglio finire con "InheritedClass" essere passato al costruttore VeryBaseClass(string className, MyObject myObject).

+1

Perché vuoi farlo?Un costruttore non dovrebbe preoccuparsi se viene chiamato per istanziare la propria classe o una classe derivata. – AakashM

+1

Penso che ti manchi il punto. L'assembly esterno che definisce la classe base di un framework che utilizzo ha richiesto la rappresentazione della stringa della classe stessa da passare nel costruttore. Finora l'ho inserito come ci si aspetterebbe (come una stringa), ma preferirei che lo risolvesse in fase di esecuzione per motivi di sanità mentale. – Codesleuth

+1

Oh capisco, non è la tua classe. Continuo a pensare che sia una cosa negativa che importi :) – AakashM

risposta

14

Ah Ah! Ho trovato una soluzione. Puoi farlo con i generici:

public abstract class VeryBaseClass 
{ 
    public VeryBaseClass(string className, MyObject myObject) 
    { 
     this.ClassName = className; 
    } 

    public string ClassName{ get; set; } 
} 
public abstract class BaseClass<T> : VeryBaseClass 
{ 
    public BaseClass(MyObject myObject) 
     : base(typeof(T).Name, myObject) 
    { 
    } 
} 

public class InheritedClass : BaseClass<InheritedClass> 
{ 
    public InheritedClass(MyObject myObject) 
     : base(myObject) 
    { 

    } 
} 
+0

Oh mio Dio, genio! Lo proverò quando avrò finito di macellare il mio layout del controllo sorgente TFS :) – Codesleuth

+0

Eccellente, funziona!Anche se significa che ho molto lavoro da fare per cambiare le mie lezioni esistenti, ma ne varrà la pena alla fine. Grazie :) – Codesleuth

+2

Improvvisamente, appare il CRTP (noto anche come modello di modello ricorrente)! :) (vedi https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) – Macke

3

Does GetType(). Nome non funziona?

+1

No, perché utilizza implicitamente "questo" che non è possibile utilizzare per valutare gli argomenti concatenati del costruttore. –

+0

Scusa, non intendevo in una catena di costruzione, intendevo dire che il costruttore di VeryBaseClass poteva usare GetType(). Nome invece di aspettarsi che fosse passato. – pdr

+0

@pdr: ciò presuppone che VeryBaseClass desideri sempre il nome del tipo corrente; mentre in alcuni casi potrebbe essere un progetto ragionevole, non è sempre così. Sto osservando questo come una domanda più ampia di "cosa fai quando vuoi usare questo? In un argomento della catena del costruttore" - perché non puoi sempre spingere la funzionalità nell'albero ereditario. –

3

Una possibile soluzione:

class VeryBase { 
    public VeryBase(string name) { 
     Console.WriteLine(name); 
    } 
} 

class Base<T> : VeryBase where T : Base<T> { 
    protected Base() 
     : base(typeof(T).Name) { 
    } 
} 

class Derived : Base<Derived> { 
    public Derived() { 
    } 
} 

class Program { 
    public static void Main(params string[] args) { 
     Derived d = new Derived(); 
    } 
} 
+0

Scusa, hai ragione, questo.GetType avrebbe dovuto essere il mio esempio. Ahimè, questo non è definito. – Codesleuth

+0

Ah, ok ... beh, ho aggiornato il mio post con una soluzione possibile (anche se non molto facile da usare) ... –

+0

Che diventa difficile er se si desidera aggiungere un altro livello di ereditarietà e si mette la logica di usare Type.Name nella classe base anziché nella classe derivata. (Nella domanda, quella logica è nello strato intermedio delle tre.) –

5

Ho avuto esattamente lo stesso dolore prima ora nella Google Wave Robot .NET API dove volevo far passare il costruttore in alcuni valori in base agli attributi. Puoi dare un'occhiata alla mia soluzione nel codice per lo derived type e lo base type. Fondamentalmente passo un delegato al costruttore di base e poi chiama quel delegato che passa "questo" al delegato. Quindi nel tuo caso si avrebbe:

public VeryBaseClass(Func<VeryBaseClass, string> classNameProvider) 
{ 
    this.name = classNameProvider(this); 
} 

e

public BaseClass() : base(FindClassName) 
{ 
} 

private static string FindClassName(VeryBaseClass @this) 
{ 
    return @this.GetType().Name; 
} 

E 'davvero brutto, ma funziona.

MODIFICA: questo approccio funziona solo se è possibile modificare il costruttore della classe base come mostrato; Se non è possibile, io non sono sicuro che sia effettivamente realizzabile a tutti :(

+0

Non ho mai visto '@ questo 'prima e sembra confondere Google. Permette semplicemente di chiamare un metodo con nessun parametro e posiziona automaticamente l'oggetto corrente in (è così che lo vedo) – Codesleuth

+1

No, il @ di fronte sta solo dicendo al compilatore di considerare 'questo' come un identificatore (per distinguerlo dalla parola chiave). questo: http://msdn.microsoft.com/en-us/library/aa664670%28VS.71%29.aspx. Puoi sostituire '@questo' con qualsiasi nome di parametro. –

+0

'Argomento '1': impossibile convertire da 'method group' a 'string'' Immagino che dovrei menzionare qui che sebbene il mio esempio mostri solo l'unico parametro usato dalla classe base, la mia implementazione effettiva ne usa ancora di più. Aggiornerò la domanda per riflettere questo. – Codesleuth

0

Ancora un'altra opzione ...

// VeryBaseClass is in an external assembly 
public abstract class VeryBaseClass 
{ 
    public VeryBaseClass(string className) 
    { 
     Console.WriteLine(className); 
    } 
} 

// BaseClass and InheritedClass are in my assembly 
public abstract class BaseClass : VeryBaseClass 
{ 
    public BaseClass(string className) 
     : 
     base(className) 
    { 

    } 
} 

public class InheritedClass : BaseClass 
{ 
    public InheritedClass() 
     : base(typeof(InheritedClass).Name) 
    { 
    } 
} 
+0

Questo equivarrebbe a digitare il nome della classe come stringa. Volevo evitare di dover passare tutto ciò che non devo nel costruttore 'BaseClass' da' InheritedClass'. – Codesleuth

3

Se la possibilità di determinare il tipo di classe ereditata in fase di esecuzione è più importante le prestazioni si può dare un'occhiata al StackTrace per vedere dove il costruttore viene chiamato da potrebbe essere necessario codificare in più ipotesi al momento della chiamata GetInheritedClassName in questo esempio:.

public abstract class BaseClass : VeryBaseClass 
{ 
    private static string GetInheritedClassName 
    { 
     get 
     { 
      // Get the current Stack 
      StackTrace currentStack = new StackTrace(); 
      MethodBase method = currentStack.GetFrame(1).GetMethod(); 

      // 1st frame should be the constructor calling 
      if (method.Name != ".ctor") 
       return null; 

      method = currentStack.GetFrame(2).GetMethod(); 

      // 2nd frame should be the constructor of the inherited class 
      if (method.Name != ".ctor") 
       return null; 

      // return the type of the inherited class 
      return method.ReflectedType.Name; 
     } 
    } 

    public BaseClass(MyObject myObject) : 
     base(GetInheritedClassName, myObject) 
    { 

    } 
} 

non riesco a pensare ad un ottimo modo per dinamicamente ottenere il nome della classe ereditata senza implicazioni sulla performance. Mi rendo conto è quello che si voleva evitare, ma se avevo bisogno di chiamare un assembly 3rd party con questi requisiti vorrei solo richiedere ogni classe di specificare il proprio tipo:

public abstract class BaseClass : VeryBaseClass 
{ 
    public BaseClass(string className, MyObject myObject) : 
     base(className, myObject) 
    { 

    } 
} 

public class InheritedClass : BaseClass 
{ 
    public InheritedClass(MyObject myObject) : base("InheritedClass", myObject) 
    { 

    } 
} 
+0

Mi piace l'intraprendenza di questa risposta, ma hai ragione riguardo all'intensità delle prestazioni. Dopo aver esaminato il mio codice, posso vedere che non costruisco molti di questi oggetti contemporaneamente in qualsiasi punto del mio codice, quindi questo è sicuramente fattibile. Sicuramente una risposta +1 :) – Codesleuth

Problemi correlati