2010-12-03 35 views
5

archivo de datos: (Data.txt) líneas representan width heightC# 4.0 Implícitamente Typed objetos dinámicos

5 
6 9 
7 2 
4 4 

C# Código:

var list = new List<dynamic>(); 
using (var sr = new StreamReader("Data.txt", Encoding.UTF8)) 
{ 
    list = sr.ReadToEnd().Split('\n').Select(r => 
    { 
     var split = r.Split(' '); 
     var len = split.Length; 
     return new { 
      w = len > 0 ? int.Parse(split[0].Trim()) : 0, 
      h = len > 1 ? int.Parse(split[1].Trim()) : 0 
     } as dynamic; 
    }).ToList(); 
} 
int Area = list.Sum(r => r.h * r.w); 

El ejemplo funciona como es. Tuve que hacer algunas cosas no deseadas para que funcione.

Primero tuve que declarar la lista para evitar el uso del ámbito: dado que no tengo un objeto de dimensión tipeado, hice el tipo dinámico (var list = new List<dynamic>()).

La parte indeseable es convertir el objeto anónimo a una dinámica (as dynamic). De lo contrario me sale

No se puede convertir implícitamente el tipo System.Collections.Generic.List<AnonymousType#1> a System.Collections.Generic.List<dynamic>

¿Por qué aparece este error? Sé que una dinámica puede contener un tipo anónimo, ¿es esto un problema con la extensión y la dinámica de ToList()?

Necesito poder acceder a los elementos de la lista anónima fuera de la instrucción using, como en la última línea que calcula el área.


Solución: Fui con la respuesta de DTB. Evita el uso de una declaración y dinámica de uso en conjunto. ¡Gracias a todos por la entrada!

var list = 
    (from line in File.ReadLines("Data.txt") 
    let parts = line.Split(' ') 
    let width = int.Parse(parts[0]) 
    let height = parts.Length > 1 ? int.Parse(parts[1]) : 0 
    select new { width, height }).ToList(); 
+0

¿Por qué utiliza dinámico como este? – ChaosPandion

+7

Uso inapropiado de dinámico. Usted * sabe * el tipo, no hay beneficio para hacerlo dinámico. Use una estructura o Tuple <>. –

+0

Entonces, ¿estás diciendo que no hay forma de hacer esto con un tipo anónimo? –

Respuesta

6

Puede usar File.ReadLines para evitar StreamReader.

IEnumerable<dynamic> query = 
    from line in File.ReadLines("Data.txt") 
    let parts = line.Split(' ') 
    let width = int.Parse(parts[0]) 
    let height = parts.Length > 1 ? int.Parse(parts[1]) : 0 
    select new { width, height } as dynamic; 

List<dynamic> list = query.ToList(); 

int area = list.Sum(t => t.width * t.height); 

Sin embargo, como otros han señalado, el uso dinámico no es realmente apropiado aquí. Si usa la consulta solo dentro de un método, una instancia anónima es suficiente. Si desea utilizar el resultado de la consulta fuera del método, cree una estructura o clase pequeña o use Tuple < T1, T2 >.

+0

Muy bien, pero aún así no deberían estar usando dinámica. – ChaosPandion

+0

Me gusta esta respuesta porque evita el uso de bloques, la razón por la que introduje la dinámica en primer lugar. –

1

Tiene que ver con co/contravarianza.

No se puede convertir List<T1> en List<T2>. Intenta con

List<object> objectList = new List<string>{"hello"}; 

y se obtiene el error de compilación "No se puede convertir implícitamente el tipo 'System.Collections.Generic.List<string>' a 'System.Collections.Generic.List<object>'"

Es para evitar el error

List<string> stringList = new List<string>{"hello"}; 
List<object> objectList = stringList; // compile error 
objectList.Add(new Car()); // this would add a Car object to the stringList if the above line was allowed 
2

me desharía de la primera línea, a continuación, donde se asigna la lista declararlo con var y mover la última línea dentro de la instrucción using:

int Area; 
using (var sr = new StreamReader("Data.txt", Encoding.UTF8)) 
{ 
    var list = sr.ReadToEnd().Split('\n').Select(r => 
    { 
     var split = r.Split(' '); 
     var len = split.Length; 
     return new { 
      w = len > 0 ? int.Parse(split[0].Trim()) : 0, 
      h = len > 1 ? int.Parse(split[1].Trim()) : 0 
     }; 
    }).ToList(); 
    Area = list.Sum(r => r.h * r.w); 
} 
+0

Creo que esta es la mejor opción aquí. Elimina el uso indeseable de 'dynamic', evita el uso de' Tuple <> 'y es funcionalmente equivalente. – LBushkin

+0

@LBushkin: Gracias :) –

+0

@LBushkin - Tengo mucho más que hacer que simplemente calcular el área. No creo que la respuesta sea escribir todo el programa en el uso. –

1

La razón por la que no puede asignar simplemente es la siguiente:

En el primer caso, obtendrá un List<SomeAnonymousType>. Ese tipo no tiene ninguna relación con List<dynamic>, incluso si existe una conversión implícita del tipo anónimo al dynamic. Es un poco como convertir implícitamente un List<int> a un List<float>.

0

Dynamic no es su solución aquí. Simplemente tiene que calcular el área dentro de la instrucción using.

Hacerlo fuera de de la instrucción using es una mala idea si está intentando introducir la ejecución diferida aquí; terminará diposing de StreamReader antes de realizar la llamada a Sum.

Al final, su código debería tener este aspecto:

int Area = 0; 

using (var sr = new StreamReader("Data.txt", Encoding.UTF8)) 
{ 
    Area = sr.ReadToEnd().Split('\n').Select(r => 
    { 
     var split = r.Split(' '); 
     var len = split.Length; 
     return new { 
      w = len > 0 ? int.Parse(split[0].Trim()) : 0, 
      h = len > 1 ? int.Parse(split[1].Trim()) : 0 
     } as dynamic; 
    }).Sum(r => r.h * r.w); 
} 

Hacerlo de esta manera es mejor, no está materializando la lista y luego un bucle a través de él de nuevo, usted está tirando de los datos y resumiendo según sea necesario.

+0

Tengo mucho más que hacer que solo calcular el área. Procederé a usar las dimensiones de la lista para otros cálculos. El área fue solo un ejemplo. No creo que la respuesta sea escribir todo el programa en el uso. –

+1

@Josiah Ruddell: No, no es así, en cuyo caso debe escribir con fuerza la estructura que está serializando desde el archivo, y luego ceder los elementos a medida que los lee del lector. Al usar el rendimiento, StreamReader no se abrirá hasta que la enumeración se complete y no se cierre hasta que se elimine la enumeración (lo que foreach hace por usted). – casperOne

3

Usted realmente debe hacer algo como esto:

private IEnumerable<Tuple<int, int>> ReadFile(string filePath, 
    Encoding encoding) 
{ 
    using (var sr = new StreamReader(filePath, encoding)) 
    { 
     string line; 
     while ((line = sr.ReadLine()) != null) 
     { 
      var split = line.Split(' '); 
      var w = split.Length > 0 ? int.Parse(split[0]) : 0; 
      var h = split.Length > 1 ? int.Parse(split[1]) : 0; 
      yield return Tuple.Create(h, w); 
     } 
    } 
} 

Ahora usted tiene una secuencia perezoso y fuertemente tipado de valores.

+0

Me gusta esta respuesta también +1 –

0

Principalmente estoy de acuerdo con otras observaciones, la dinámica probablemente no es la de aquí, sino la discusión, pero como Albin señala, se trata de parámetros genéricos que son invariables a menos que se especifique explícitamente con 'in' o 'out' modificadores.

Usted puede hacer su trabajo de código mediante la adición de .Cast() antes de llamar a .ToList()

Cuestiones relacionadas