2010-02-18 4 views
8

Wie greife ich die Type der geerbten Klasse und übergeben Sie es in den Basiskonstruktor der Klasse auch geerbt? Sehen Sie sich das folgende Codebeispiel:Übergeben aktuellen Objekttyp in Basis Konstruktor Anruf

// 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) 
    { 

    } 
} 

Die Linie base(typeof(this).Name, myObject) funktioniert nicht, weil ich nicht this noch verweisen kann, da das Objekt nicht beendet hat, Konstruktion und deshalb nicht existiert.

Ist es möglich, die Type des gerade konstruierenden Objekts zu greifen?

EDIT:

die Probe als orsogufo suggested, wird korrigiert, aber immer noch nicht funktioniert, wie this nicht definiert ist.

EDIT 2:

Nur um zu klären, möchte ich mit "InheritedClass" am Ende wird in den Konstruktor VeryBaseClass(string className, MyObject myObject) geben.

+1

Warum möchten Sie dies tun?Ein Konstruktor sollte sich nicht darum kümmern, ob er aufgerufen wird, seine eigene Klasse oder eine abgeleitete Klasse zu instanziieren. – AakashM

+1

Ich denke, du verpasst den Punkt. Die externe Assembly, die die Basisklasse eines von mir verwendeten Frameworks definiert, benötigt die Zeichenfolgendarstellung der Klasse von sich selbst, die an den Konstruktor übergeben wird. Bisher habe ich es so eingegeben, wie Sie es erwarten würden (als String), aber ich würde es vorziehen, wenn es zur Laufzeit um der Vernunft willen funktionieren würde. – Codesleuth

+1

Oh ich sehe, es ist nicht deine Klasse. Ich denke immer noch, dass es eine schlechte Sache ist, dass es sich interessiert :) – AakashM

Antwort

14

Ah Hah! Ich habe eine Lösung gefunden. Sie können es mit Generika machen:

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 mein Gott, Genie! Ich werde es testen, wenn ich mit dem TFS Source Control Layout fertig bin :) – Codesleuth

+0

Ausgezeichnet, es funktioniert!Obwohl es bedeutet, dass ich viel Arbeit haben muss, um meine bestehenden Klassen zu ändern, aber es wird es am Ende wert sein. Danke :) – Codesleuth

+2

Plötzlich erscheint das CRTP (aka seltsam wiederkehrendes Vorlagenmuster)! :) (siehe https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) – Macke

3

Funktioniert GetType(). Name nicht?

+1

Nein, weil das implizit "this" verwendet, das Sie nicht zum Auswerten von Konstruktorverkettungsargumenten verwenden können. –

+0

Entschuldigung, ich meine nicht in einer Konstruktor-Kette, ich meinte, dass der Konstruktor von VeryBaseClass GetType() verwenden könnte, anstatt zu erwarten, dass es übergeben wird. – pdr

+0

@pdr: Das setzt voraus, dass VeryBaseClass immer den Namen des aktuellen Typs will; Während das in einigen Fällen ein vernünftiger Entwurf sein kann, ist es nicht immer so. Ich betrachte dies als eine umfassendere Frage "Was tun Sie, wenn Sie' this 'in einem Konstruktorkettenargument verwenden wollen "- weil Sie die Funktionalität nicht immer in den Vererbungsbaum schieben können. –

3

Eine mögliche Lösung:

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

Entschuldigung, du hast recht Mein Beispiel, leider ist "das" nicht definiert – Codesleuth

+0

Ah, ok ... nun, ich habe meinen Beitrag mit einer möglichen (wenn auch nicht sehr einfach zu bedienenden) Lösung aktualisiert ... –

+0

Das wird tricki Wenn Sie eine weitere Vererbungsebene hinzufügen möchten, wird die Logik verwendet, Type.Name in der Basisklasse anstelle der abgeleiteten Klasse zu verwenden. (In der Frage ist diese Logik in der mittleren Schicht der drei.) –

5

ich genau vor jetzt die gleichen Schmerzen gehabt haben in der Google Wave Robot .NET API, wo ich auf Attribute den Konstruktor in einigen Werten basierend machen wollte. Sie können sich meine Lösung im Code für die derived type und die base type ansehen. Im Grunde übergebe ich einen Delegaten an den Basiskonstruktor und rufe dann diesen Delegaten auf, der "this" an den Delegaten übergibt. Also in Ihrem Fall müssten Sie:

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

und

public BaseClass() : base(FindClassName) 
{ 
} 

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

Es ist wirklich hässlich, aber es funktioniert.

EDIT: Dieser Ansatz funktioniert nur, wenn Sie Ihren Basisklassenkonstruktor wie gezeigt ändern können; wenn Sie kann nicht, ich bin nicht sicher, es tatsächlich machbar ist bei :(alle

+0

Ich habe '' this'' noch nie gesehen und scheint Google zu verwirren. Erlaubt es nur, dass eine Methode aufgerufen wird keine Parameter und setze das aktuelle Objekt automatisch in? (so sehe ich es) – Codesleuth

+1

Nein, das @ davor sagt nur, dass der Compiler "dieses" als Bezeichner betrachtet (um es vom Schlüsselwort zu unterscheiden) dies: http://msdn.microsoft.com/en-us/library/aa664670%28VS.71%29.aspx. Sie könnten "@this" durch einen beliebigen Parameternamen ersetzen. –

+0

'Argument '1': kann nicht konvertiert werden "Methodengruppe" zu "String" Ich sollte hier erwähnen, dass, obwohl mein Beispiel nur den einen Parameter zeigt, der von der Basisklasse verwendet wird, meine tatsächliche Implementierung ein paar mehr verwendet. Ich werde die Frage aktualisieren, um dies widerzuspiegeln. – Codesleuth

0

noch eine weitere Option ...

// 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

Dies wäre genauso wie das Eingeben des Klassennamens als String. Ich wollte vermeiden, dass ich etwas, das ich nicht muss, in den 'BaseClass'-Konstruktor aus der' InheritedClass' übergeben muss. – Codesleuth

3

Wenn die Fähigkeit, die geerbte Klasse Typ zur Laufzeit zu bestimmen, ist wichtiger als Leistung Sie können an der Stacktrace einen Blick darauf werfen, um zu sehen, wo der Konstruktor aus aufgerufen wird, Sie müssen unter Umständen mehr Annahmen codieren, wenn GetInheritedClassName in diesem Beispiel nennen:.

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) 
    { 

    } 
} 

ich kann nicht eine gute Möglichkeit zu denken, dynamisch Erhalte den Namen der geerbten Klasse ohne Auswirkungen auf die Leistung. Ich weiß, ist, was man vermeiden wollte, aber wenn ich mit diesen Anforderungen eine 3rd-Party-Versammlung zu rufen brauchte ich würde nur jede Klasse benötigen, um ihre eigene Art angeben:

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

Ich mag die Einfallsreichtum dieser Antwort, aber Sie haben Recht, dass es leistungsintensiv ist. Nachdem ich meinen Code überprüft habe, kann ich sehen, dass ich nicht viele dieser Objekte gleichzeitig an irgendeinem Punkt in meinem Code konstruiere, also ist dies definitiv machbar. Definitiv eine +1 Antwort :) – Codesleuth

Verwandte Themen