2010-08-09 11 views
19

Estoy teniendo dificultades para encontrar lo que, creo, debería ser un método bastante simple.C# In() método? (como Sql)

Creo que todos hemos utilizado la siguiente:

select someThing from someTable where someColumn in('item1', 'item2') 

En C#, he tiene que escribir cosas como esta:

if (someEnum == someEnum.Enum1 || someEnum == someEnum.Enum2 || 
    someEnum == someEnum.Enum3) 
{ 
    this.DoSomething(); 
} 

Esto funciona, pero es sólo prolijo.

Por frustración, escribí un método de extensión para lograr lo que estoy tratando de hacer.

namespace System 
{ 
    public static class SystemExtensions 
    { 
     public static bool In<T>(this T needle, params T[] haystack) 
     { 
      return haystack.Contains(needle); 
     } 
    } 
} 

Ahora, puedo escribir código más corto:

if (someEnum.In(someEnum.Enum1, someEnum.Enum2, someEnum.Enum3)) 
    this.DoSomething(); 
if (someInt.In(CONSTANT1, CONSTANT2)) 
    this.DoSomethingElse(); 

Se siente sucio, sin embargo, para escribir mi propio método para algo que simplemente no puedo encontrar en el marco.

cualquier ayuda que la gente puede ofrecer sería grande, Gracias

EDIT: Gracias a todos por el anaylsis en profundidad. Creo que seguiré usando mi método In().

+0

No hay nada en el marco específicamente para hacer esto, por lo que su enfoque parece sólido. también podrías hacer [] {algúnEnum.Enum1, SomeEnum.Enum2} .Contains (someEnum} nuevo si no quieres el método de extensión. Sin embargo, el método de extensión lo hace leer mejor. – btlog

+3

Generalmente uso 'Contiene()' método de extensión para * In * consultas. De hecho, 'Contains()' se traduce a 'in' en consultas de Linq-to-SQL.Me gusta el método de extensión 'In()', ya que proporciona una envoltura sintáctica agradable para 'Contains()' para usar cuando aún no tengo los valores en un contenedor enumerable. – kbrimington

+0

Parece que ya entendiste por qué los métodos de extensión están allí en primer lugar. Me gusta la limpieza de tu enfoque. –

Respuesta

7

No existe un método de extensión como el que tiene. Permítanme explicar por qué creo que es (aparte de la razón obvia "porque no fue especificada, implementada, probada, documentada, etc.").

Básicamente, esta implementación es necesariamente ineficiente. La construcción de una matriz a partir de los parámetros pasados ​​a In (como ocurre cuando usa la palabra clave params) es una operación O (N) y causa una presión de GC gratuita (a partir de la construcción de un nuevo objeto T[]). Contains luego enumera sobre esa matriz, lo que significa que su código original se ha más que duplicado en tiempo de ejecución (en lugar de una enumeración parcial mediante evaluación en cortocircuito, tiene una enumeración completa seguida de una enumeración parcial).

La presión GC causados ​​por la construcción array podría aliviarse algo mediante la sustitución de la versión params del método de extensión con sobrecargas X teniendo de 1 a parámetros X de tipo T donde X es un número razonable ... como 1 -2 docenas. Pero esto no cambia el hecho de que está pasando valores X a un nuevo nivel de la pila de llamadas solo para verificar potencialmente menos de X de ellos (es decir, no elimina la penalización de rendimiento, solo la reduce).

Y luego hay otro problema: si intenta utilizar este método de extensión In para reemplazar una serie de comparaciones encadenadas ||, hay algo más que podría estar pasando por alto. Con ||, obtiene una evaluación de cortocircuito; lo mismo no vale para los parámetros pasados ​​a los métodos. En el caso de una enumeración, como en su ejemplo, esto no importa. Pero tenga en cuenta este código:

if (0 == array.Length || 0 == array[0].Length || 0 == array[0][0].Length) 
{ 
    // One of the arrays is empty. 
} 

Lo anterior (mala raro/- sólo para ilustración) código no debería lanzar una IndexOutOfRangeException (que podría lanzar una NullReferenceException, pero eso es irrelevante para el punto que estoy haciendo). Sin embargo, el código "equivalente" usando In podría muy bien:

if (0.In(array.Length, array[0].Length, array[0][0].Length) 
{ 
    // This code will only be reached if array[0][0].Length == 0; 
    // otherwise an exception will be thrown. 
} 

No estoy diciendo que su In idea de extensión es una mala. En la mayoría de los casos, si se usa correctamente, puede ahorrar en la escritura y el costo de rendimiento/memoria no será notable. Solo ofrezco mis reflexiones sobre por qué un método de este tipo no sería apropiado como método de biblioteca incorporado: porque sus costos y limitaciones probablemente no se entenderían, lo que llevaría a un uso excesivo y un código subóptimo.

+0

¿Cómo sabes que es O (N)? – mathk

+0

@mathk - Bueno, si tiene n elementos, debe agregar cada uno de esos n elementos a la matriz. No hay manera de que puedas ignorar los artículos; si lo hace, no ha creado una matriz de todos los elementos n por definición. Entonces tiene que ser al menos O (n). – jloubert

+0

Dado que es una matriz inmutable, puede dejarlo en la pila sin necesidad de mover elementos. No sé cómo ha salido C#, pero podría ser O (1) – mathk

0

Puede que le interese el FlagAttibute si quiere hacer esto particularmente con Enums.

+0

Ese atributo solo tiene semántica cosmética. – leppie

+0

@leppie El marco utiliza este atributo en la implementación ToString de la enumeración cuando el enumerante no coincide con ningún valor, y es útil como documentación. – Trillian

+1

-1 No servirá de nada. El atributo Flags trata con un problema completamente ortogonal, a saber, la composición de múltiples valores enum en una sola variable. El OP tiene solo un valor en la variable. – siride

0

Los idiomas no pueden complacer a todos, pero si lo hace, o si el compilador lo hace, no hay mucha diferencia. El idioma que da Any & Contains

En podría estar bien en su mundo, pero cuando alguien tiene que recoger su código será confuso para ellos.

1

Creo que está cerca al usar la llamada Contains.

List<strong> items = List<string>{ "item1", "item2", "item3" }; 
bool containsItem = items.Contains("item2"); 

Este es el enfoque común para las consultas de Linq.

from item in ... 
where items.contains(item) 
select item 

BTW: Me gusta su método de extensión, creo que podría ser extremadamente útil en determinadas situaciones.

+0

él está usando contiene en su método de extensión. –

1

Eso es todo. Su método de extensión In() es bastante agradable. Incluso si está utilizando LINQ, que está modelado después de SQL, aún tiene que usar Contains para indicar usando IN en el SQL.

from a in table 
where SomeArray.Contains(a.id) 
select a; 

se traduce en:

select * from table a where a.id in (.....) 
1

no sé nada más.

Para mí, creo que está bien escribir los métodos de extensión que usaste, para operaciones que a menudo necesitas y quieres una sintaxis fácil de leer. Para eso son buenos los métodos de extensión.

Existen cientos de métodos de extensión útiles.Podría preguntar por muchos de ellos, ¿por qué no están incluidos en .NET Framework?

No todo puede estar ya incluido en un idioma. Así que escriba su propia biblioteca y espere que se incluya en el futuro.

0

Podría hacer algo un poco mejor, al usar Expressions, esto permitirá que la construcción se utilice correctamente en casos como Linq2Sql.

0

Puede utilizar el método de extensión .Intersect si desea que se devuelvan distintos valores. P.ej.

List<string> test = new List<string>() { "1", "2", "2", "3" }; 
List<string> test2 = new List<string>() { "1", "2" }; 

var results = test.Intersect<string>(test2);