2010-08-09 8 views
19

J'ai du mal à trouver ce qui, je pense, devrait être une méthode assez simple.C# In() méthode? (comme Sql)

Je pense que nous avons tous utilisé ceci:

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

En C#, j'ai dois écrire des trucs comme ça:

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

Cela fonctionne, mais il est juste verbeux. Par frustration, j'ai écrit une méthode d'extension pour accomplir ce que j'essaie de faire.

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

Maintenant, je peux écrire un code plus court:

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

Il se sent sale, cependant, d'écrire ma propre méthode pour quelque chose que je ne peux pas trouver dans le cadre.

Toute aide que vous pouvez offrir des gens serait génial, Merci

EDIT: Merci à tous pour la anaylsis en profondeur. Je pense que je continuerai à utiliser ma méthode In().

+0

Il n'y a rien dans le framework spécifiquement pour cela, donc votre approche semble solide. vous pouvez aussi faire new [] {someEnum.Enum1, someEnum.Enum2} .Contains (someEnum} si vous ne voulez pas la méthode d'extension.La méthode d'extension rend la lecture plus agréable quand même – btlog

+3

J'utilise généralement le 'Contains()' méthode d'extension pour les requêtes * In * En fait, 'Contains()' se traduit par 'in' dans les requêtes Linq-to-SQL.J'aime votre méthode d'extension 'In()', car elle fournit un bon wrapper syntaxique pour 'Contains()' à utiliser quand je n'ai pas déjà les valeurs dans un conteneur énumérable. – kbrimington

+0

Il semble que vous ayez compris pourquoi les méthodes d'extension sont là en premier lieu. J'aime la propreté de ton approche. –

Répondre

7

Il n'existe aucune méthode d'extension existante comme celle que vous avez. Permettez-moi d'expliquer pourquoi je pense que c'est (en dehors de la raison évidente "parce que ce n'était pas spécifié, mis en œuvre, testé, documenté, etc").

Fondamentalement, cette implémentation est nécessairement inefficace. La construction d'un tableau à partir des paramètres passés à In (comme cela se produit lorsque vous utilisez le mot-clé params) est une opération O (N) et provoque une pression GC gratuite (à partir de la construction d'un nouvel objet T[]). Contains énumère ensuite sur ce tableau, ce qui signifie que votre code d'origine a plus que doublé en temps d'exécution (au lieu d'une énumération partielle via une évaluation en court-circuit, vous avez une énumération complète suivie d'une énumération partielle).

La pression de GC causés par la construction du tableau pourrait être allégées quelque peu en remplaçant la version params de la méthode d'extension avec les surcharges X prenant de 1 à paramètres X de type T où X est un nombre raisonnable ... comme 1 -2 douzaines. Mais cela ne change pas le fait que vous transmettez des valeurs X à un nouveau niveau de la pile d'appels uniquement pour vérifier potentiellement moins de X (c'est-à-dire, cela n'élimine pas la pénalité de performance, seulement la réduit).

Et puis, il y a un autre problème: si vous avez l'intention que cette méthode d'extension In serve de remplacement à un tas de comparaisons || enchaînées, il y a quelque chose d'autre que vous pourriez négliger. Avec ||, vous obtenez une évaluation en court-circuit; il n'en va pas de même pour les paramètres passés aux méthodes. Dans le cas d'une énumération, comme dans votre exemple, cela n'a pas d'importance. Mais considérez ce code:

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

ci-dessus (bizarre/mauvais - pour illustration) code ne doit pas jeter un IndexOutOfRangeException (il pourrait lancer une NullReferenceException, mais ce n'est pas pertinent au point que je fais). Cependant, le code « équivalent » à l'aide In pourrait très 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. 
} 

Je ne dis pas que votre In idée d'extension est une mauvaise. Dans la plupart des cas, lorsqu'il est utilisé correctement, il peut économiser sur la frappe et le coût de performance/mémoire ne sera pas perceptible. Je ne fais qu'expliquer pourquoi une méthode de ce type ne serait pas appropriée en tant que méthode de bibliothèque intégrée: parce que ses coûts et ses limites seraient probablement mal compris, conduisant à une utilisation excessive et à un code sous-optimal.

+0

Comment savez-vous que O (N)? – mathk

+0

@mathk - Eh bien, si vous avez n éléments, vous devez ajouter chacun de ces n éléments au tableau. Vous ne pouvez pas ignorer les éléments. si vous le faites, vous n'avez pas créé un tableau de tous les n éléments par définition. Donc, il doit être au moins O (n). – jloubert

+0

Comme il s'agit d'un tableau immuable, vous pouvez le laisser sur la pile, pas besoin de déplacer des objets. Donno comment C# l'a fait sortir mais il pourrait être O (1) – mathk

0

Vous pourriez être intéressé par le FlagAttibute si vous voulez le faire particulièrement avec Enums.

+0

Cet attribut n'a qu'une sémantique cosmétique. – leppie

+0

@leppie L'infrastructure utilise cet attribut dans l'implémentation ToString de l'énumération lorsque l'énumérateur ne correspond à aucune valeur, et elle est utile en tant que documentation. – Trillian

+1

-1 Ça ne va pas aider du tout. L'attribut Flags traite d'un problème entièrement orthogonal, à savoir la composition de plusieurs valeurs d'énumération dans une seule variable. L'OP n'a qu'une valeur dans la variable. – siride

0

Les langages ne peuvent pas plaire à tout le monde, mais que vous le fassiez ou que le compilateur le fasse il n'y a pas beaucoup de différence. La langue que vous donne Any & Contains

Dans peut-être bien dans votre monde, mais quand quelqu'un d'autre doit venir chercher votre code, il sera source de confusion pour eux.

1

Je pense que vous êtes proche d'utiliser l'appel Contains.

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

Cette approche est courante pour les requêtes Linq.

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

BTW: J'aime votre méthode d'extension, je pense que cela pourrait être extrêmement utile dans certaines situations.

+0

il utilise Contains dans sa méthode d'extension. –

1

C'est à peu près tout. Votre méthode d'extension In() est plutôt sympa. Même si vous utilisez LINQ, qui est modélisé après SQL, vous devez toujours utiliser Contains pour indiquer l'utilisation de IN dans SQL.

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

traduit:

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

Je ne sais rien d'autre. Pour moi, je pense qu'il est juste d'écrire de telles méthodes d'extension comme vous l'avez fait, pour les opérations dont vous avez souvent besoin et que vous voulez une syntaxe lisible et pratique. C'est ce que les méthodes d'extension sont bonnes pour.

Il existe juste des centaines de méthodes d'extension utiles.Vous pouvez demander beaucoup d'entre eux, pourquoi ne sont-ils pas inclus dans le framework .NET?

Tout ne peut pas être déjà inclus dans une langue. Alors écrivez votre propre bibliothèque et espérez qu'elle sera incluse dans le futur.

0

Vous pourriez faire quelque chose d'un peu mieux, en utilisant Expressions, cela permettra à la construction d'être correctement utilisée dans des cas comme Linq2Sql.

0

Vous pouvez utiliser la méthode d'extension .Intersect si vous souhaitez que des valeurs distinctes soient renvoyées. Par exemple.

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

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