2012-09-17 42 views
12

decir, que tienen 2 clases:¿Por qué Enumerable.Cast no utiliza moldes definidos por el usuario?

public class A 
{ 
    public int a; 
} 

public class B 
{ 
    public int b; 

    public static implicit operator B(A x) 
    { 
     return new B { b = x.a }; 
    } 
} 

Entonces, ¿por

A a = new A { a = 0 }; 
B b = a; //OK 

List<A> listA = new List<A> { new A { a = 0 } }; 
List<B> listB = listA.Cast<B>().ToList(); //throws InvalidCastException 

Lo mismo para explicit operador.

P.S .: fundición cada elemento manualmente (separetely) funciona

List<B> listB = listA.Select<A, B>(s => s).ToList(); //OK 

Respuesta

10

El nombre de Enumerable.Cast es engañoso, ya que su propósito es Unbox valores. Funciona en IEnumerable (no en IEnumerable<T>) para producir un IEnumerable<T>. Si ya tiene un IEnumerable<T>Enumerable.Cast es muy probable que no sea el método que desea utilizar.

Técnicamente, se está haciendo algo como esto:

foreach(object obj in value) 
    yield return (T)obj; 

Si T es otra cosa que el valor en caja, esto conducirá a una InvalidCastException.

Puede probar este comportamiento sí mismo:

int i = 0; 
object o = i; 
double d1 = (double)i; // Works. 
double d2 = (double)o; // Throws InvalidCastException 

Usted tiene dos soluciones posibles:

  1. Use Select(x => (B)x)
  2. Crear un método de extensión Cast que trabaja en una IEnumerable<T> en lugar de un IEnumerable.
+0

Entonces, ¿falla porque intenta lanzar 'object' a' B', mientras que yo definí un molde de 'A' a' B'? – horgh

+0

No funciona, porque primero necesitaría desempaquetar el 'objeto' en una' A' y luego hacer el molde. Algo como '(B) (A) o'. Podría definir un operador de conversión que trabaje en 'objeto' en lugar de' A', pero eso no tendría sentido ya que la mayoría de las instancias de 'objeto' no serían convertibles. –

+0

Pero es una 'Lista ' donde 'T' es' A'. No es un 'ArrayList' o' List '. ¿Por qué tiene lugar este problema de boxeo/desempaquetado? – horgh

Cuestiones relacionadas