2008-09-23 19 views
43

No estoy seguro de si esto es algo extraño de hacer o no, o si es de alguna manera el olor del código ... pero me preguntaba si había alguna manera (algún tipo de patrón de oops sería bueno) para "lanzar" un tipo de base a una forma de su tipo derivado. Sé que esto tiene poco sentido ya que el tipo derivado tendrá una funcionalidad adicional que el padre no ofrece, que en sí mismo no es fundamentalmente sólido. Pero, ¿hay alguna forma de hacer esto? Aquí es un ejemplo de código para que yo pueda explicar mejor lo que "estoy pidiendo.Una forma de convertir un tipo de base en un tipo derivado

public class SomeBaseClass { 
    public string GetBaseClassName {get;set;} 
    public bool BooleanEvaluator {get;set;} 
} 

public class SomeDerivedClass : SomeBaseClass { 
    public void Insert(SqlConnection connection) { 
      //...random connection stuff 
      cmd.Parameters["IsItTrue"].Value = this.BooleanEvalutar; 
      //... 
    } 
} 

public static void Main(object[] args) { 
    SomeBaseClass baseClass = new SomeBaseClass(); 
    SomeDerivedClass derClass = (SomeDerivedClass)baseClass; 
    derClass.Insert(new sqlConnection()); 
} 

Sé que esto parece tonto, pero ¿hay alguna manera de lograr algo de este tipo?

+1

El uso del término "emitir" en esta pregunta es incorrecto. La conversión implica que el objeto que lanzamos ya es un objeto de este tipo. Por ejemplo: objeto o = 3; MethodWithIntegerParam ((int) o); En su pregunta baseClass no es una instancia con clase derivada. Entonces, lo que estás buscando es conversión y no conversión. – AXMIM

Respuesta

30

No profundamente, en" "Idiomas manejados. Esto es downcasting, y no hay una manera correcta de manejarlo, exactamente por la razón que usted describió (las subclases proporcionan más que clases base, ¿de dónde viene este" más "?). Si realmente quiere un comportamiento similar para una jerarquía particular, puede usar constructores para tipos derivados que tomarán el tipo base como prototipo.

Se podría construir algo con la reflexión que maneja los casos simples (tipos más específicos que no tienen un estado de adición). En general, solo rediseña para evitar el problema.

Editar: Woops, no puede escribir operadores de conversión entre tipos base/derivados. Una rareza de Microsoft tratando de "protegerte" contra ti mismo. Ah, bueno, al menos no están tan mal como el sol.

+0

No puede utilizar los operadores de conversión/conversión para este caso. – leppie

+0

'Ah, bueno, al menos no están tan mal como el sol. - ¿Qué quieres decir? Solo por curiosidad; No sé Java. (Sé que han pasado 4 años). –

+4

@KonradMorawski Me refería a las características del lenguaje Java diseñadas para garantizar que escriba un código "bueno" (donde "bueno" es su opinión). Mi más odiada siendo las excepciones marcadas (por lo que * no * puedes ignorar las excepciones, nunca). –

0

Eso no puede funcionar. Ve a la página de ayuda vinculada por el error de compilación.

La mejor solución es utilizar aquí los métodos de fábrica.

+0

Lol ... Sé que estaba tratando de hacer mi proceso de pensamiento ... créanme, lo intenté: D –

+0

Adam Wright lo explica mejor :) – leppie

+0

"Eso no puede funcionar :) ".Net lo permite y es posible usar C#. Ver http://stackoverflow.com/a/15987692/1497385 –

0

Esto se conoce como downcasting y la sugerencia de Seldaek de utilizar la versión "segura" es correcta.

Aquí hay una bastante decente description with code samples.

9

Downcasting tiene sentido, si tiene un objeto de clase derivada pero está referenciado por una referencia de tipo de clase base y por alguna razón desea que vuelva a ser referenciado por una referencia de tipo de clase derivada. En otras palabras, puede abatir para revertir el efecto de upcasting anterior. Pero no puede tener un objeto de clase base referenciado por una referencia de un tipo de clase derivado.

+0

Es interesante que cuando "invierte" el upcast, toda la información derivada "extra" es accesible de nuevo. Claramente, se retuvo en el montón de memoria de alguna manera. –

+0

He estado leyendo sobre este tema.La razón por la cual la información se conserva en ese objeto es que el objeto en sí no se cambia cuando lo modifica, solo la referencia al mismo. Y RE esta respuesta, parece una razón perfectamente legítima para abatir. Especialmente cuando se trata de SOA. –

5

No, esto no es posible. En un lenguaje administrado como C#, simplemente no funcionará. El tiempo de ejecución no lo permitirá, incluso si el compilador lo deja pasar.

usted mismo ha dicho que esto parece tonto:

SomeBaseClass class = new SomeBaseClass(); 
SomeDerivedClass derClass = (SomeDerivedClass)class; 

Así que pregúntate, es class realmente una instancia de SomeDerivedClass? No, entonces la conversión no tiene sentido. Si necesita convertir SomeBaseClass a SomeDerivedClass, debe proporcionar algún tipo de conversión, ya sea un constructor o un método de conversión.

Parece que su jerarquía de clases necesita algún trabajo. En general, no debería ser ser posible convertir una instancia de clase base en una instancia de clase derivada. En general, debe haber datos y/o funcionalidades que no se aplican a la clase base. Si la funcionalidad de clase derivada se aplica a todas las instancias de la clase base, entonces se debe enrollar en la clase base o incorporar a una nueva clase que no forme parte de la jerarquía de la clase base.

+0

"No, esto no es posible. En un lenguaje administrado como C#, simplemente no funcionará". .Net lo permite y es posible hacerlo con C#. Ver http://stackoverflow.com/a/15987692/1497385 –

0

Esto no es posible porque ¿cómo vas a obtener el "extra" que tiene la clase derivada? ¿Cómo sabría el compilador que se refiere a derivedClass1 y no derivedClass2 cuando crea una instancia?

Creo que lo que realmente está buscando es el patrón de fábrica o similar para que pueda crear instancias de objetos sin saber realmente el tipo explícito que se está instanciando. En su ejemplo, tener el método "Insertar" sería una interfaz de la instancia que la fábrica devuelve implementa.

1

Sí, este es un olor a código, y casi todo indica que su cadena de herencia está rota.

Mi conjetura (de la muestra limitada) es que prefiere tener DerivedClass operan en una instancia de SomeBaseClass - de modo que "DerivedClass tiene una SomeBaseClass", en lugar de "DerivedClass es un SomeBaseClass" . Esto se conoce como "favorecer la composición sobre la herencia".

17

¡Pruebe la composición en lugar de la herencia!

Me parece como que sería mejor pasar una instancia de SomeBaseClass a la SomeDerivedClass (que ya no derivan de la clase base, y debería denominarse como tal)

public class BooleanHolder{  
    public bool BooleanEvaluator {get;set;} 
} 

public class DatabaseInserter{ 
    BooleanHolder holder; 

    public DatabaseInserter(BooleanHolder holder){ 
     this.holder = holder; 
    } 

    public void Insert(SqlConnection connection) { 
      ...random connection stuff 
      cmd.Parameters["IsItTrue"].Value = holder.BooleanEvalutar; 
      ... 
    } 
} 

public static void Main(object[] args) { 
    BooleanHolder h = new BooleanHolder(); 
    DatabaseInserter derClass = new DatabaseInserter(h); 
    derClass.Insert(new sqlConnection); 
} 

Salida http://www.javaworld.com/javaworld/jw-11-1998/jw-11-techniques.html (página 3): reutilización

Código a través de la composición composición proporciona una forma alternativa para Apple reutilizar implementación de la cáscara de la fruta(). En lugar de extender Fruit, Apple puede contener una referencia a una instancia de Fruit y definir su propio método peel() que simplemente invoca peel() en Fruit.

+16

Aunque prefiero la composición sobre la herencia, la idea de que "Apple tiene una fruta" en lugar de "Apple es una fruta" es el peor ejemplo que he visto en mi vida. para ello. Si Apple define su propio método Peel, entonces ya no es sustituible por Fruit, que borra todo el concepto de Fruit. –

+1

En lugar de "Apple tiene fruta", podría leerla como "Apple tiene las propiedades de ser una fruta", que suena mucho mejor y, de hecho, es más precisa que "Apple es fruta". –

+0

Argumentaría que "la propiedad de ser una fruta" no sería composición ni herencia, pero la implementación de una interfaz :) – HaMster

1

Como han notado otros, la pieza que sugiere no es realmente posible. ¿Sería tal vez un caso en el que se puede introducir el Decorator pattern (extracto de Head First)?

+0

.Net lo permite y es posible hacerlo usando C#. Ver http://stackoverflow.com/a/15987692/1497385 –

1

¿Ha pensado en una interfaz que implementarían actualmente su clase base y su clase derivada? No sé los detalles de por qué está implementando de esta manera, pero podría funcionar.

0

No sé por qué nadie ha dicho esto y es posible que haya olvidado algo, pero puede usar la palabra clave como y si necesita hacer una instrucción if, use if.

SomeDerivedClass derClass = class as SomeDerivedClass; //derClass is null if it isnt SomeDerivedClass 
if(class is SomeDerivedClass) 
    ; 

operación -Editar- I asked this question long ago

-1

C++ maneja usando un constructor. C++ Typecasting. Parece un descuido para mí. Muchos de ustedes han sacado a relucir la cuestión de qué haría el proceso con las propiedades adicionales. Yo respondería, ¿qué hace el compilador cuando crea la clase derivada cuando el programador no establece las propiedades? He manejado esta situación similar a C++. Creo un constructor que toma la clase base y luego establece manualmente las propiedades en el constructor. Esto es definitivamente preferible a establecer una variable en la clase derivada y romper la herencia. También lo elegiría sobre un método de fábrica porque creo que el código resultante sería más limpio.

0

Recientemente he tenido la necesidad de extender un DTO simple con un tipo derivado para poner algunas propiedades más en él. Luego quise volver a utilizar la lógica de conversión que tenía, desde los tipos de bases de datos internas hasta los DTO.

La forma Solucioné era mediante la aplicación de un constructor vacío en las clases DTO, utilizando de esta manera:

class InternalDbType { 
    public string Name { get; set; } 
    public DateTime Date { get; set; } 
    // Many more properties here... 
} 

class SimpleDTO { 
    public string Name { get; set; } 
    // Many more properties here... 
} 

class ComplexDTO : SimpleDTO { 
    public string Date { get; set; } 
} 

static class InternalDbTypeExtensions { 
    public static TDto ToDto<TDto>(this InternalDbType obj) where TDto : SimpleDTO, new() { 
     var dto = new TDto { 
      Name = obj.Name 
     } 
    } 
} 

I puede entonces volver a utilizar la lógica de conversión de la simple DTO al convertir al complejo uno. Por supuesto, tendré que completar las propiedades del tipo complejo de alguna otra manera, pero con muchas, muchas propiedades del DTO simple, esto realmente simplifica las cosas de la OMI.

7

No estoy diciendo que recomiendo esto. Pero podría convertir la clase base en una cadena JSON y luego convertirla a la clase derivada.

SomeDerivedClass layer = JsonConvert.DeserializeObject<SomeDerivedClass>(JsonConvert.SerializeObject(BaseClassObject)); 
+2

* "No estoy diciendo que recomiendo esto." * – twip

15

Personalmente, no creo que valga la pena el problema de usar herencia en este caso. En su lugar, simplemente pase la instancia de la clase base en el constructor y acceda a ella a través de una variable miembro.

private class ExtendedClass //: BaseClass - like to inherit but can't 
{ 
    public readonly BaseClass bc = null; 
    public ExtendedClass(BaseClass b) 
    { 
     this.bc = b; 
    } 

    public int ExtendedProperty 
    { 
     get 
     { 
     } 
    } 
} 
+0

Me pareció muy útil, especialmente al transformar una lista de clases base en otra lista con más funciones en cada una elemento de lista. –

+0

Pero en este caso es imposible llamar a los métodos de clase base, debido a que ExtendedClass no los tiene en absoluto –

+1

Sí puede: extendedObject.BaseClass.BaseClassMethod (args) –

5

lenguaje C# no permite este tipo de operadores, pero todavía se puede escribirlas y trabajan:

[System.Runtime.CompilerServices.SpecialName] 
public static Derived op_Implicit(Base a) { ... } 

[System.Runtime.CompilerServices.SpecialName] 
public static Derived op_Explicit(Base a) { ... } 
+0

cómo funcionaría la operación implícita si asigna la Lista = Lista ? –

+0

@BoppityBop funcionaría igual que 'List = List ' o 'List = List ' - no funcionaría. Sin embargo, puedes crear tu propia colección con algunos operadores de conversión implícitos. –

+0

solo para ser claro. Está diciendo que 'op_Implicit' según su solución * no se llamará * cuando base/derived esté en una colección genérica. ¿derecho? –

0

Como muchas respuestas han señalado, no se puede abatido que tiene sentido total.

Sin embargo, en su caso, SomeDerivedClass no tiene propiedades que "perderán". Por lo que podría crear un método de extensión de esta manera:

public static T ToDerived<T>(this SomeBaseClass baseClass) 
    where T:SomeBaseClass, new() 
{ 
    return new T() 
    { 
     BooleanEvaluator = baseClass.BooleanEvaluator, 
     GetBaseClassName = baseClass.GetBaseClassName 
    }; 
} 

por lo que no está echando, simplemente convertir:

SomeBaseClass b = new SomeBaseClass(); 
SomeDerivedClass c = b.ToDerived<SomeDerivedClass>(); 

Esto sólo funciona si todos los datos de la clase base se encuentra en el forma de propiedades legibles y editables.

Cuestiones relacionadas