2010-12-15 154 views
2

Comment comparez-vous les enums ayant plusieurs bits? Je dois manquer quelque chose de simple.Comparer deux enums w/bitwise pour un seul vrai résultat?

J'ai une valeur persistante cible d'un enum, et j'ai les paramètres de l'utilisateur de cette même énumération. J'ai besoin de comparer les deux pour voir s'il y a une seule correspondance d'un ou plusieurs bits enum définis.

Bonus: J'aimerais utiliser ici les opérateurs bit à bit pour raccourcir la requête linq (parce que je la réplique 5 ou 6 fois pour différentes propriétés). Je sais que ce n'est pas facile à lire, mais cela aiderait vraiment la performance dans ce que je fais.

public enum Targets 
{ 
    NotSet = 0, 

    Anonymous = 1, 
    Everyone = 2 
    Adult = 4, 
    Child = 8, 

    LikesFishing = 16 
} 

Je les utilisateurs avec de multiples objectifs fixés:

var loggedInUser = new User() 
{ 
    Username = "eduncan911", 
    Targets = Targets.Everyone | Targets.Adult | Targets.LikesFishing 
};  

J'ai des articles définis avec plusieurs cibles différentes:

var article1 = new Article() 
{ 
    Title = "Announcement for Parents and Children", 
    Targets = Targets.Adult | Targets.Child 
}; 

var article2 = new Article() 
{ 
    Title = "What fishing boat do you own?", 
    Targets = Targets.LikesFishing | Targets.Adult 
}; 

var article3 = new Article() 
{ 
    Title = "Be nice to your parents!", 
    Targets = Targets.Child 
}; 

Comment puis-je interroger des articles qui a 1 cible bit défini qui correspond au moins à 1 des cibles de l'utilisateur spécifié ci-dessus (1 ou plus)? Je devrais récupérer les deux premiers articles parce qu'ils correspondent à Targets.Adult - mais le loggedInUser.Targets ne correspond à aucun bit dans le 3ème ensemble de cibles.

Je sais que je peux interroger des articles pour un type Enum spécifique, comme celui-ci:

var articles = 
    db.Articles.Where(x => x.Targets.HasFlag(Targets.LikesFishing); 

Mais, je n'ai pas une seule cible - j'ai plusieurs bits mis. Par conséquent, le fait de transmettre simplement "loggedInUser.Targets" ne correspondra à aucun car la valeur stockée est juste un int.

Au début, j'Interrogation pour énumérations comme ceci:

// returns a collection of enums the user has set 
// in their profile. 
var loggedInUserEnums = 
    Enum.GetValues(typeof(Targets)) 
    .Cast<Targets>() 
    .Where(x => loggedInUser.Targets.HasFlag(x)); 

Mais lorsque l'on compare la collection à l'autre collection de quels articles ont mis, je toujours en revenir vrai pour chaque article. Je pense que je partais pour la terre.

Y at-il une opération de bits que je peux passer dans l'expression linq db.Articles.Where (...) de comparer les deux? Juste une supposition, mais je remarque quand je demande des cibles d'article que j'ai NotSet retournant vrai aussi - peu importe I ~ Targets.NotSet ou pas. Impair.

+0

nourriture supplémentaire pour la pensée: Ai-je besoin de boucler les loggedInUserEnums un nd requête pour chaque Enum? Cela me semble coûteux quand je pense qu'il devrait y avoir une requête bit à bit pour cela. – eduncan911

+1

Est-il sûr de supposer que par "Linq" vous voulez dire linq à sql ou un autre fournisseur de requête de base de données linq? – marr75

+0

Je m'excuse. Je veux dire "Lambda", pas linq. – eduncan911

Répondre

6

si vous voulez voir les articles où la cible est soit LikesFishing ou adulte,

essayez ceci:

var target = Targets.LikesFishing | Targets.Adult; 
var articles = db.Articles.Where(x => (int)(x.Targets & target) > 0); 

et oh, oui, ajoutez le [FlagsAttribute] au ENUM:

[Flags] 
public enum Targets 
{ NotSet = 0, Anonymous = 1, Everyone = 2, 
    Adult = 4, Child = 8, LikesFishing = 16 } 
+0

Intéressant, le casting vers int peut fonctionner. Je vais essayer de vous le faire savoir. – eduncan911

+0

Oui, ça a marché! – eduncan911

+0

En fait, le casting à int n'est pas nécessaire, fyi. – eduncan911

0

Tout d'abord, vous voulez vous assurer que vous attribuez cette enum avec des drapeaux:

[Flags] 
public enum Targets 
{ 
    NotSet = 0, 

    Anonymous = 1, 
    Everyone = 2 
    Adult = 4, 
    Child = 8, 

    LikesFishing = 16 
} 

secondes, votre LINQ ressemblerait à ceci:

var articlesThatLikeFishing = db.Articles.Where(x => (x.Targets & Targets.LikesFishing) == Targets.LikesFishing) 
+0

Dans votre requête linq, vous spécifiez Targets.LikesFishing. Je ne peux pas faire cela, car tout ce que j'ai est la collection de loggedInUser.Targets. C'est tout. – eduncan911

+0

Les drapeaux modifient le ToString et lui permettent d'interagir avec d'autres langages CLR (vb). – marr75

0

Je pense que vous vouliez dire pour décorer votre Enum avec un décorateur de drapeaux.De plus, en fonction du fournisseur linq que vous utilisez, cette fonctionnalité peut être implémentée ou non. En outre, selon le fournisseur linq que vous utilisez. Le fournisseur doit être capable de décomposer l'expression pour produire une clause where appropriée et certains fournisseurs ne peuvent pas gérer ce que vous demandez. Cela vous laisse avec quelques options.

Vous pouvez écrire des méthodes qui généreront une expression que votre fournisseur peut gérer. Ceci est probablement quelque part dans le sens de .Where (x => x.Targets == 1 || x.Targets = 3 ou x.Targets = 5 ... etc Votre code ne ressemblera pas à ceci, car vous générerez dynamiquement l'expression (c'est un sujet plus avancé, voir l'article this)

Vous pouvez, plus simplement, déplacer les cibles enum vers leur propre objet avec un identifiant et une description. Joignez-vous aux avec plusieurs à plusieurs et sur cette requête

-1

Je vais utiliser int au lieu de ENUM, quelque chose comme:.

class Program { 
    public const int NotSet = 1; 
    public const int Anonymous = 1 << 2; 
    public const int Everyone = 1 << 3; 
    public const int Adult = 1 << 4; 
    public const int Child = 1 << 5; 
    public const int LikesFishing = 1 << 6; 

    public static bool HasFlag(Article article, int flag) { 
     return (article.Targets & flag) != 0; 
    } 

    public static bool HasFlags(Article article, params int[] flags) { 
     foreach (int flag in flags) { 
      if ((article.Targets & flag) == 0) return false; 
     } 
     return true; 
    } 

    static void Main(string[] args) { 
     var article1 = new Article() { 
      Title = "Announcement for Parents and Children", 
      Targets = Adult | Child 
     }; 

     var article2 = new Article() { 
      Title = "What fishing boat do you own?", 
      Targets = LikesFishing | Adult 
     }; 

     var article3 = new Article() { 
      Title = "Be nice to your parents!", 
      Targets = Child 
     }; 

     List<Article> db = new List<Article>() { article1, article2, article3 }; 

     var articles = 
      db.Where(x => HasFlag(x, LikesFishing)); 

     foreach (Article article in articles) { 
      Console.WriteLine(article.Title); 
     } 
    } 
} 

class Article { 
    public string Title { get; set; } 
    public int Targets { get; set; } 
}