Сравните два перечисления w / bitwise для одного истинного результата? - PullRequest
2 голосов
/ 16 декабря 2010

Как вы сравниваете перечисления с несколькими установленными битами?Я, должно быть, упускаю что-то простое.

У меня целевое постоянное значение одного перечисления, и у меня есть настройки пользователя того же перечисления.Мне нужно сравнить два, чтобы увидеть, есть ли одно совпадение с одним или несколькими установленными битами перечисления.

Бонус: я хотел бы использовать здесь побитовые операторы для сокращения запроса linq (потому что я повторяю это 5 или6 раз за разные свойства).Я знаю, что это нелегко прочитать, но это действительно поможет производительности в том, что я делаю.

public enum Targets
{
  NotSet = 0,

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

  LikesFishing = 16
}

У меня есть пользователи с несколькими установленными целями:

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

У меня есть статьи снесколько разных целей:

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
};

Как запросить статьи, для которых установлен 1 бит цели, который соответствует как минимум 1 цели указанного выше пользователя (1 или более)?Я должен получить первые две статьи, потому что они соответствуют Targets.Adult - но loggedInUser.Targets не соответствует ни одному биту в третьем наборе целей.

Я знаю, что могу запрашивать статьи для определенного типа Enum,как это:

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

Но у меня нет единственной цели - у меня установлено несколько битов.Следовательно, передача только «loggedInUser.Targets» никогда не будет соответствовать никому, поскольку сохраненное значение является просто целым числом.

Сначала я запрашивал перечисления следующим образом:

// 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));

Носравнивая коллекцию с другой коллекцией статей, которые я установил, я всегда возвращался к каждой статье.Я думаю, что отправляюсь в ла-ла-лэнд.

Есть ли побитовая операция, которую я могу передать в выражение linq db.Articles.Where (...) для сравнения двух?

Просто предположение, но я замечаю, когда я запрашиваю цели статьи, у меня NotSet также возвращает true - независимо от того, какой я ~ Targets.NotSet или нет.Одд.

Ответы [ 4 ]

6 голосов
/ 16 декабря 2010

, если вы хотите увидеть статьи, где целью является LikesFishing или Adult,

попробуйте это:

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

и, о, да, добавьте [FlagsAttribute] в перечисление:

[Flags]
public enum Targets 
{   NotSet = 0,  Anonymous = 1, Everyone = 2,
    Adult = 4,   Child = 8,    LikesFishing = 16 }
0 голосов
/ 16 декабря 2010

Я буду использовать int вместо enum, что-то вроде:

    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; }
}
0 голосов
/ 16 декабря 2010

Я думаю, вы хотели украсить свой Enum декоратором флагов.

Кроме того, в зависимости от того, какой поставщик linq вы используете, эта функция может быть или не быть реализована. Поставщик должен иметь возможность разбить выражение, чтобы создать соответствующее предложение where, и некоторые поставщики не могут обработать то, что вы просите. Это оставляет вам пару вариантов.

Вы можете написать методы, которые сгенерируют выражение, которое сможет обработать ваш провайдер. Это, вероятно, где-то в духе .Where (x => x.Targets == 1 || x.Targets = 3 или x.Targets = 5 ... и т.д. потому что вы будете динамически генерировать выражение (это более сложная тема, см. эту статью).

Вы можете, проще говоря, переместить перечисление целей в свой собственный объект с идентификатором и описанием. Затем вы можете присоединиться к ним со связями «многие ко многим» и задать вопрос по этому вопросу.

0 голосов
/ 16 декабря 2010

Прежде всего вы должны убедиться, что вы указали это перечисление с флагами:

[Flags]
public enum Targets
{
    NotSet = 0,

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

    LikesFishing = 16
}

Секунды, ваш LINQ будет выглядеть так:

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