2012-05-10 10 views
5

nunca he hecho esto antes, y aunque no puedo pensar en una razón específica que se rompería, me gustaría comprobar que es válido utilizar una variable de la siguiente manera:¿Es seguro usar las variables en las llamadas dentro de las declaraciones LINQ?

void Main() 
{ 
    var types = new [] { typeof(A), typeof(B) }; 
    bool b = false; 
    var q = from type in types 
      from property in type.GetProperties() 
      let propertyName = GetName(property, out b) 
      select new { 
       TypeName = type.Name, 
       PropertyName = propertyName, 
       PropertyType = property.PropertyType.Name, 
       IsNullable = b 
      }; 
    q.Dump(); 
} 

private string GetName(PropertyInfo property, out bool isNullable) 
{ 
    string typeName; 
    isNullable = false; 
    var type = property.PropertyType; 
    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) 
    { 
     isNullable = true; 
     typeName = type.GetGenericArguments().First().Name; 
    } 
    else 
    { 
     typeName = property.Name; 
    } 
    return typeName; 
} 
+2

Esto se consideraría la peor práctica. – asawyer

+0

@asawyer, ¿Qué, en particular, hace que digas eso? – sblom

+0

@asawyer, a diferencia de ¿qué? –

Respuesta

10

Esto funcionará, siempre que evalúe completamente la consulta.

Sin embargo, el comportamiento será muy extraño, y sería algo que evitaría fuertemente. Como el parámetro out se usa directamente en la consulta, el comportamiento será bastante normal aquí (siempre que no haga nada más con esto), pero eso es específico de este caso de uso, no una "regla" general con el uso de out mezclado con LINQ.

El problema es que la ejecución diferida de LINQ hará que se establezca el parámetro de salida, pero solo cuando use el enumerable resultante, no cuando lo declare. Esto puede causar un comportamiento muy inesperado y llevar a un software difícil de mantener y comprender.

yo personalmente acaba de escribir un método separado, y lo utilizan para permitir la consulta para ser escrita como:

var q = from type in types 
     from property in type.GetProperties() 
     let propertyName = GetName(property) 
     let nullable = GetIsNullable(property) 
     // ... 

Esto es mucho más clara, y menos propenso a errores y equivocaciones. También funcionará con la paralelización (es decir: PLINQ a través de .AsParallel()) y otras técnicas si alguien intenta cambiar esto más tarde.

+3

También hay una gran advertencia sobre cualquier cosa que pueda causar una evaluación simultánea, como 'Parallel', ya que la' b' se comparte. –

+0

@MarcGravell Definitivamente - He editado para mostrar una alternativa que no tendrá esos problemas exactamente por esa razón. –

3

Es legal desde el punto de vista semántico hacerlo, pero si es seguro depende mucho de cómo lo haga. El peligro fundamental aquí es que está combinando la asignación de un local con una expresión que es demorada, y posiblemente nunca, ejecutada.

En este caso, la llamada a GetName en realidad nunca puede suceder si la colección está vacía. Por lo tanto, siempre puede mantener su valor original de false (esta es también la razón por la que el compilador C# obliga a declarar aquí un valor predeterminado). Si esta semántica está bien con su programa, entonces el uso de b está perfectamente bien. De hecho, parece estar en este escenario, ya que b solo se usa después de llamar al método.

Sin embargo, esto es algo que evitaría en general. Es muy fácil equivocarse de tal manera que solo fallaría en casos de esquina.

3

Esto funcionará, aproximadamente, pero por cualquier razón incorrecta (y es un mal hábito de recoger, ya que no es seguro en el caso más general). Una idea más seguro sería una tupla:

 let info = GetInfo(property) 
     select new { 
      TypeName = type.Name, 
      PropertyName = info.Item1, 
      PropertyType = property.PropertyType.Name, 
      IsNullable = info.Item2 
     }; 

.... 

private Tuple<string,bool> GetInfo(PropertyInfo property) 
{ 
    string typeName; 
    bool isNullable = false; 
    ... 
    return Tuple.Create(typeName, isNullable); 
} 

Para los escenarios más complejos, un tipo con propiedades sensiblemente nombre sería aún mejor.

+0

Hola, fui con la mayoría de votos para marcar la respuesta, pero quería decirle que me gusta mucho su solución, ya que elude el problema que me llevó a recurrir a una var out en primer lugar –

+0

@Aaron no hay problema; 2 opciones son mejores que 1 de todos modos :) –

Cuestiones relacionadas