2010-02-09 22 views
9

tengo una clase X y una clase Y, el último, que se deriva de X:C# polimorfismo simple pregunta

class x {} 
class y : x {} 

Entonces en algún lugar estoy usando una lista de X:

List<X> lstX; 
... 

Entonces 'd como para utilizar una nueva lista de Y, a partir de los datos en mi otra lista ... algo por el estilo:

List<Y> lstY = lstX; 

creería que los elementos de la lista de X que está Me convertiré automáticamente en Y, pero ese no es el caso.

Además, ¿cómo podría inicializar una nueva instancia de Y, desde una cierta X? Me gustaría hacer:

var newX = new X(); 
var newY = new Y(X); 

pero parece que no funciona así.

Gracias por su ayuda! y lo siento por el formato, haciendo lo mejor

+0

duplicado posible: http://stackoverflow.com/questions/2231593/upcasting-with-a-generic-type-parameter –

Respuesta

27

Hay un par de preguntas aquí.

Primero: "Puedo asignar un objeto de tipo Tiger a una variable de tipo Animal. ¿Por qué no puedo asignar un objeto de tipo List of Tiger a una variable de tipo List of Animal?"

Porque entonces esto sucede:

List<Tiger> tigers = new List<Tiger>(); 
List<Animal> animals = tigers; // this is illegal because if we allow it... 
animals.Add(new Giraffe()); // ... then you just put a giraffe into a list of tigers. 

En C# 4 Será legal para hacer esto:

IEnumerable<Tiger> tigers = new List<Tiger>(); 
IEnumerable<Animal> animals = tigers; 

Esto es legal porque IEnumerable<T> tiene ningún método "Agregar" y por lo tanto esto está garantizado para estar seguro.

Ver mi serie de artículos sobre la covarianza para obtener más información sobre esta nueva característica de C# 4.

http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

Segunda pregunta: "¿cómo podría inicializar una nueva instancia de Tigre, a partir de un determinado animal"

No puede. El Animal en cuestión podría ser una Mariquita. ¿Cómo vas a inicializar un nuevo Tiger de una instancia de Ladybug? Eso no tiene ningún sentido, así que no te permitimos hacerlo. Si desea escribir su propio método especial que sabe cómo convertir animales arbitrarios en tigres, puede hacerlo. Pero no sabemos cómo hacer eso por ti.

+1

Dagnammit esa es una buena respuesta, no podría haberlo dicho mejor. De hecho, para probarlo, no lo hice. Maldito seas, Eric Lippert. –

+0

excelente respuesta de hecho, muchas gracias. – ibiza

+0

Aunque para mí, ejemplificar una Lista de animales con un grupo de Tigres, y luego agregar algunas Jirafas me parece legítimo, ya que tienes una Lista de animales allí. Simplemente sucede que lo inicializaste con algunos Tigres. Debe ser mi lógica entonces que debo cambiar. – ibiza

1

No, ya que no puede estar seguro de que todos los elementos de Listx, que son del tipo 'X', son también de tipo Y.
La relación de herencia es a la inversa en torno a: un elemento de tipo Y se puede lanzar a X, porque Y es-un X.

en C#, también hay "copia constructores disponibles como en C++, por lo que me temo que vas a tener para implementar esa lógica, para poder inicializar una nueva instancia de Y, de una cierta X, ustedes mismos. Además, tenga en cuenta que las clases son tipos de referencia ...

3

Eso nunca va a funcionar; después de todos List<Y> lstY = lstX; sólo copia la referencia (a menos que agregue su propio operador de conversión estática implícita a su propio tipo de lista) - por lo que es aún una lista de X y podría contener otros cosas de Y casos.

Incluso en 4.0, la varianza co/contra no se extiende a a: listas (in y out), o b: tipos concretos (como List<T>).

Curiosamente, (y siempre lo ha hecho) funciona para matrices de tipos de referencia, pero solo en la dirección X[] arrX = arrY;. No convierte nada; si tratas de poner los datos incorrectos arrojará una excepción.

+0

Incluso eso no funcionará, ya que Y es una X, pero X no es una Y. –

+0

@Frederik - Ah, sí, sí. Mi error. Fijo. –

0

Su situación es un poco atrás de los casos de uso polimórficos habituales. Y es una X, pero X no es una Y.

Teniendo esto en cuenta podría forzar funciona de la forma en que lo dice colocando código de clonación en constructores y otras cosas.

0

El problema que tendrá con su primer ejemplo es que Y deriva de X, por lo que "es una X", pero "X no es una Y", también C# no admite actualmente la conversión de tipos en este método . Puede intentar usar métodos de extensión para Cast, como lstX.ConvertAll como ayudante para lograr esto.

Para la segunda pregunta que desee ver en un constructor de copia, tales como:

public Y(X baseObject) { 
    //copy all the data you need here... 
} 
1

Puede no de forma automática "ensanchar" el tipo de los objetos de x a y, a causa X no es Y, pero Y es X.

Usted podría tratar de fundición de X a Y, pero que fallará con un InvalidCastException a menos que su objeto era originalmente una Y haciéndose pasar por una X el principio. Es necesario para inicializar y poblar un nuevo List<Y> manualmente:

IList<Y> yList = new List<Y>(); 
foreach (var x in xList) 
{ 
    var y = new Y(x); // Copies/clones inherited properties from x to a new Y 
    // TODO: Set other properties of y not present on x 
    yList.Add(y); 
} 
1

Prueba esto:


using System.Collections.Generic; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      List myList; 

      myList = GetMyList(MyListTypeEnum.MyList1); 
      myList = GetMyList(MyListTypeEnum.MyList2); 
     } 

     public static List GetMyList(MyListTypeEnum tipo) 
     { 
      List result; 
      result = new List(); 

      switch (tipo) 
      { 
       case MyListTypeEnum.MyList1: 
        List myList1 = GetMyList1(); 
        foreach (var item in myList1) 
        { 
         result.Add((IMyList) item); 
        } 
        break; 
       case MyListTypeEnum.MyList2: 
        List myList2 = GetMyList2(); 
        foreach (var item in myList2) 
        { 
         result.Add((IMyList) item); 
        } 
        break; 
      } 

      return result; 
     } 

     public static List GetMyList1() 
     { 
      List myList1 = new List(); 
      myList1.Add(new MyList1 { Code = 1 }); 
      myList1.Add(new MyList1 { Code = 2 }); 
      myList1.Add(new MyList1 { Code = 3 }); 
      return myList1; 
     } 

     public static List GetMyList2() 
     { 
      List myList2 = new List(); 
      myList2.Add(new MyList2 { Name = "1" }); 
      myList2.Add(new MyList2 { Name = "2" }); 
      myList2.Add(new MyList2 { Name = "3" }); 
      return myList2; 
     } 
    } 

    public interface IMyList 
    { 
    } 

    public class MyList1 : IMyList 
    { 
     public int Code { get; set; } 
    } 

    public class MyList2 : IMyList 
    { 
     public string Name { get; set; } 
    } 

    public enum MyListTypeEnum 
    { 
     MyList1, 
     MyList2 
    } 
}