2010-02-18 6 views
8

¿Cómo tomo el Type de la clase heredada y lo paso al constructor base de la clase también heredado? Vea el ejemplo de código siguiente:Pasar el tipo de objeto actual a la llamada del constructor 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 línea base(typeof(this).Name, myObject) no funciona porque no puedo hacer referencia a this sin embargo, ya que el objeto no ha terminado de construir y por lo tanto no existe.

¿Es posible tomar el Type del objeto que está construyendo actualmente?

EDIT:

Corregido la muestra como orsogufo suggested, pero aún así no funciona, como this no está definido.

EDIT 2:

Solo para aclarar, quiero terminar con "InheritedClass" que se pasa en el VeryBaseClass(string className, MyObject myObject) constructor.

+1

¿Por qué desea hacer esto?Un constructor no debería importar si se lo llama para crear una instancia de su propia clase o una clase derivada. – AakashM

+1

Creo que estás perdiendo el punto. El ensamblaje externo que define la clase base de un marco que uso requirió que la representación de cadena de la clase de sí mismo se pasara al constructor. Hasta el momento lo he estado poniendo como es de esperar (como una cadena) pero preferiría si fuera a funcionar en tiempo de ejecución por el bien de la salud mental. – Codesleuth

+1

Oh, ya veo, no es tu clase. Sigo pensando que es malo que le importe :) – AakashM

Respuesta

14

Ah Hah! Encontré una solución. Puede hacerlo con genéricos:

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 Dios mío, genio! Lo probaré cuando termine de eliminar mi diseño de TFS Source Control :) – Codesleuth

+0

¡Excelente, funciona!Aunque significa que tengo mucho trabajo por hacer para cambiar mis clases existentes, pero valdrá la pena al final. Gracias :) – Codesleuth

+2

De repente, aparece el CRTP (también conocido como Curiously Recurring Template Pattern). :) (ver https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) – Macke

3

¿GetType(). El nombre no funciona?

+1

No, porque eso implícitamente usa "esto" que no puedes usar para evaluar los argumentos de encadenamiento del constructor. –

+0

Lo siento, no quise decir en una cadena de constructores, quise decir que el constructor de VeryBaseClass podría usar GetType(). Nombre en lugar de esperar que se pase. – pdr

+0

@pdr: eso supone que VeryBaseClass siempre quiere el nombre del tipo actual; Si bien puede ser un diseño razonable en algunos casos, no siempre es así. Estoy viendo esto como una pregunta más amplia de "¿qué haces cuando quieres usar' this' en un argumento de la cadena de constructores? "Porque no siempre puedes empujar la funcionalidad al árbol de herencia. –

3

Una posible solución:

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

Lo siento, tienes razón. Esto. GetType debería haber sido mi ejemplo. Por desgracia, 'this' no está definido. – Codesleuth

+0

Ah, vale ... bueno, he actualizado mi publicación con una posible (aunque no muy fácil de usar) solución ... –

+0

Eso se convierte en tricki er si desea agregar otra capa de herencia, y pone la lógica de usar Type.Name en la clase base en lugar de la clase derivada. (En la pregunta, esa lógica está en la capa intermedia de los tres). –

5

que he tenido exactamente el mismo dolor antes de ahora en el Google Wave Robot .NET API donde quería hacer pasar el constructor en algunos valores basados ​​en atributos. Puede echar un vistazo a mi solución en el código para derived type y base type. Básicamente pasé un delegado al constructor base, y luego llamé a ese delegado que pasaba "esto" al delegado. Por lo que en su caso se tendría:

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

y

public BaseClass() : base(FindClassName) 
{ 
} 

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

Es muy feo, pero funciona.

EDITAR: Este enfoque solo funciona si puede cambiar el constructor de su clase base como se muestra; si no puedo, no estoy seguro de que es realmente factible en absoluto :(

+0

Nunca había visto '@ this' antes, y parece confundir a Google. ¿Simplemente permite que se llame un método con no hay parámetros, y coloca automáticamente el objeto actual en? (así es como yo lo veo) – Codesleuth

+1

No, el @ delante de él solo dice que el compilador considera "esto" como un identificador (para distinguirlo de la palabra clave). esto: http://msdn.microsoft.com/en-us/library/aa664670%28VS.71%29.aspx. Puede reemplazar '@this' con cualquier nombre de parámetro. –

+0

'Argumento '1': no ​​se puede convertir de 'method group' to 'string'' Supongo que debo mencionar aquí que, aunque mi ejemplo muestra solo el parámetro que utiliza la clase base, mi implementación real usa algunas más. Actualizaré la pregunta para reflejar esto. – Codesleuth

0

Sin embargo, otra opción ...

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

Esto sería lo mismo que escribir el nombre de la clase como una cadena. Quería evitar tener que pasar algo que no tengo que hacer al constructor 'BaseClass' de' InheritedClass'. – Codesleuth

3

Si la capacidad de determinar el tipo de la clase heredada en tiempo de ejecución es más importante que rendimiento que puede echar un vistazo a la StackTrace para ver donde el constructor está siendo llamado desde es posible que tenga que codificar en más supuestos cuando se llama a GetInheritedClassName en este ejemplo:.

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

    } 
} 

no puedo pensar en una gran manera de forma dinámica obtener el nombre de la clase heredada sin implicaciones de rendimiento. Me dan cuenta es lo que quería evitar, pero si necesitaba para llamar a una asamblea del partido tercero de estos requisitos que acaba de requerir cada clase para especificar su propio 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

Me gusta el ingenio de esta respuesta, pero tiene razón sobre el hecho de que requiere mucho rendimiento. Después de revisar mi código, puedo ver que no construyo muchos de estos objetos a la vez en ningún punto de mi código, por lo que definitivamente es viable. Definitivamente una respuesta +1 :) – Codesleuth

Cuestiones relacionadas