2010-03-26 12 views
24

Digamos que tenemos una porción de código como este:Cómo obtener el tipo real de una clase derivada de su interfaz padres

IProduct product = ProductCreator.CreateProduct(); //Factory method we have here 
SellThisProduct(product); 

//... 

private void SellThisProduct(IProduct product) 
{ 
    //.. Do something here 
} 

//... 

internal class Soda : IProduct 
{} 

internal class Book : IProduct 
{} 

¿Cómo puedo inferir cuál es el producto que realmente se pasó a SellThisProduct() en el método ?

Creo que si digo GetType() o algo así probablemente devolverá el tipo de IProduct.

Respuesta

44

GetType le consigue el tipo de tiempo de ejecución exacto de un objeto. Desde el documentation:

la instancia de tipo que representa el tipo exacto de tiempo de ejecución de la instancia actual.

También puede utilizar is para determinar si un objeto es una instancia de un tipo específico:

var noise = (obj is Velociraptor) ? "SKREEE!" : "<unknown>"; 

¿Por qué necesita el tipo de ejecución exacta, sin embargo? El objetivo de una interfaz es que debe ocultar los detalles de implementación detrás de la interfaz común. Si necesita realizar una acción según el tipo, es una gran pista de que está violando la encapsulación que proporciona.

Una alternativa es el uso de polimorfismo:

public interface IVocalizer { string Talk(); } 

public class Doorbell : IVocalizer { 
    public string Talk() { return "Ding-dong!" } 
} 
public class Pokemon : IVocalizer { 
    public string Talk() { 
    var name = this.GetType().ToString(); 
    return (name + ", " + name + "!").ToUpper(); } // e.g., "PIKACHU, PIKACHU!" 
} 
public class Human : IVocalizer { 
    public string Talk() { return "Hello!"; } 
} 

Dado que estos tres tipos no están relacionados en absoluto, herencia de un tipo común no tiene sentido. Pero para representar que comparten la misma capacidad de hacer ruido, podemos usar la interfaz IVocalizer, y luego pedir a cada uno que haga ruido. Este es un enfoque mucho más limpio: ahora que no es necesario preocuparse de qué tipo es el objeto cuando se quiere pedir que se haga un ruido:

IVocalizer talker = new ???(); // Anything that's an IVocalizer can go here. 

// elsewhere: 
Console.WriteLine(talker.Talk()); // <-- Now it doesn't matter what the actual type is! 
            // This will work with any IVocalizer and you don't 
            // need to know the details. 
+1

Por cierto Pokemon dices: D – Tarik

+2

Acabo de darme cuenta de cuánto polimorfismo malo he cometido en mi aplicación. ¡MAL programador! Tengo un montón de declaración de caso para volver a configurar. – BigChrisDiD

+0

Excepto si es un tipo que admite nulos, entonces GetType devuelve el * Tipo subyacente * del objeto, no el tipo anulable –

1

typeof (producto) devolverá IProduct.

product.GetType() realmente devolverá el tipo derivado del objeto ya que es una función miembro.

+0

Aunque es cierto que 'GetType()' devolverá el tipo derivado, que no tiene nada que ver con ser una función miembro, per se. (Podría escribir fácilmente un método estático llamado 'TypeGet()' que haría lo mismo.) –

+7

'typeof (producto)' no se compilará - el operador 'typeof' espera un tipo, no una instancia. 'typeof (IProduct)' devolverá, por supuesto, IProduct. – EMP

8

Object.GetType devuelve el exact runtime type de la instancia. Eso es lo que deberías usar.

Aunque, en general, no debería importarle en absoluto cuál es el tipo de interfaz de tiempo de ejecución: si está escribiendo código para determinarlo, probablemente refleje un error en su diseño en alguna parte.

De hecho, el nombre de la interfaz IProduct ya es un poco olor a código. No está mal, per se, pero las interfaces están destinadas a definir las acciones disponibles en un objeto particular, es decir, lo que hace. El nombre IProduct parece describir lo que es, no lo que hace, que es más apropiado para una clase base abstracta. Esta no es una "regla", pero es una buena guía a seguir.

Al escribir métodos/clases que dependen de un tipo abstracto (clase base o interfaz), si encuentra que depende de los tipos más derivados o implementaciones específicas, significa que su tipo abstracto es anémico (no no tiene suficiente funcionalidad para ser utilizado efectivamente), o su dependencia tiene demasiado acoplamiento (depende de los detalles de implementación en lugar de las abstracciones).

considerar la ampliación del Product/IProduct hacer más, o hacer su dependencia en realidad el trabajo en tipos de productos específicos a través de la sobrecarga de métodos.

+0

Solo para su información, si está tratando con un proxy WCF, entonces GetType() no devuelve el tipo de tiempo de ejecución exacto de la instancia. Devuelve una interfaz como el tipo del objeto concreto real que tiene. Creo que esto tiene algo que ver con un objeto proxy transparente. – Eddie

3
if (product is Book) 
{ 
    ... 
} 
else if (product is Soda) 
{ 
    ... 
} 
4

Mientras GetType() devolverá el tipo real , debe usar el operador is.

Normalmente, no debería necesitar hacer esto. Normalmente, lo que hace es simplemente diferir el comportamiento a la clase secundaria e invocarlo a través de la interfaz. Por ejemplo, si tiene diferentes requisitos para Soda vs. Libro (por ejemplo, Soda requiere la recaudación de impuestos, mientras que el Libro no), entonces crearía un método de Venta en la interfaz y luego en su método SellThisProduct(), usted ' simplemente llame al método Sell() en el objeto.

public interface IProduct 
{ 
    public decimal Sell(); // computes price 
    ... 
} 

.....

IProduct product = ProductCreator.CreateProduct(); //Factory Method we have here 
SellThisProduct(product); 

//... 

private void SellThisProduct(IProduct product) 
{ 
    var price = product.Sell(); 
    ... 
} 

internal class Soda : IProduct 
{ 
    public decimal Sell() 
    { 
     this.Price + TaxService.ComputeTax(this.Price); 
    } 
} 

internal class Book : IProduct 
{ 
    public decimal Sell() 
    { 
     return this.Price; 
    } 
} 
Cuestiones relacionadas