1

我收集了具有一个枚举属性列表的项目。原始属性看起来像

public class Content {
    List<State> States {get; set;}
}

其中“状态”是具有近 15 个选项的枚举。

在迭代 Content 对象的集合时,我想检查它的 States 属性是否具有某些值,例如 State.Important 和 State.Updated 存在于 States 中,并从中设置另一个字符串。

就像是

if(item.States.Has(State.Important) && item.States.Has(State.Updated))
string toProcess = "Do";

如何使用 Linq 或 Lambda 做到这一点?

4

7 回答 7

4

如果您必须使用 Linq,这应该可以工作:

if (item.States.Any(state => state == State.Important) && item.States.Any(state => state == State.Updated))

否则Contains()就像@ElRonnoco 说的那样使用。

(但是,如果您的州是旗帜(2 的幂),那么这个答案会略有不同。)

这种方法的问题在于,如果没有设置任何状态,它会完全迭代集合两次。如果这种情况经常发生,它会比它可能的慢。

您可以在没有 linq 的情况下一次性解决它,如下所示:

bool isUpdated = false;
bool isImportant = false;

foreach (var state in item.States)
{
    if (state == State.Important)
        isImportant = true;
    else if (state == State.Updated)
        isUpdated = true;

    if (isImportant && isUpdated)
        break;
}

if (isImportant && isUpdated)
{
    // ...
}

这不太可能成为问题,除非您有非常大的列表,而这些列表通常没有设置任何一个目标状态,因此您可能最好还是使用 El Ronnoco 的解决方案。

如果你有很多状态要处理,你可以通过编写一个扩展方法来简化事情,如下所示:

public static class EnumerableExt
{
    public static bool AllPredicatesTrueOverall<T>(this IEnumerable<T> self, params Predicate<T>[] predicates)
    {
        bool[] results = new bool[predicates.Length];

        foreach (var item in self)
        {
            for (int i = 0; i < predicates.Length; ++i)
                if (predicates[i](item))
                    results[i] = true;

            if (results.All(state => state))
                return true;
        }

        return false;
    }

我很难想出这个名字。true如果对于每个谓词,序列中至少有一项谓词为真,它将返回。但这对于方法名称来说有点长......;)

那么您的示例将变为:

if (item.States.AllPredicatesTrueOverall(s => s == State.Important, s => s == State.Updated))

下面是一些使用它的示例代码:

enum State
{
    Unknown,
    Important,
    Updated,
    Deleted,
    Other
}

void run()
{
    IEnumerable<State> test1 = new[]
    {
        State.Important, 
        State.Updated, 
        State.Other, 
        State.Unknown
    };

    if (test1.AllPredicatesTrueOverall(s => s == State.Important, s => s == State.Updated))
        Console.WriteLine("test1 passes.");
    else
        Console.WriteLine("test1 fails.");

    IEnumerable<State> test2 = new[]
    {
        State.Important, 
        State.Other, 
        State.Other, 
        State.Unknown
    };

    if (test2.AllPredicatesTrueOverall(s => s == State.Important, s => s == State.Updated))
        Console.WriteLine("test2 passes.");
    else
        Console.WriteLine("test2 fails.");

    // And to show how you can use any number of predicates:

    bool result = test1.AllPredicatesTrueOverall
    (
        state => state == State.Important,
        state => state == State.Updated,
        state => state == State.Other,
        state => state == State.Deleted
    );
}

但也许最简单的方法是为IEnumerable<State>(如果您只需要担心一个状态枚举)编写一个扩展方法:

public static class EnumerableStateExt
{
    public static bool AllStatesSet(this IEnumerable<State> self, params State[] states)
    {
        bool[] results = new bool[states.Length];

        foreach (var item in self)
        {
            for (int i = 0; i < states.Length; ++i)
                if (item == states[i])
                    results[i] = true;

            if (results.All(state => state))
                return true;
        }

        return false;
    }
}

然后您的原始代码将变为:

if (item.States.AllStatesSet(State.Important, State.Updated))

您可以轻松指定更多状态:

if (item.States.AllStatesSet(State.Important, State.Updated, State.Deleted))
于 2013-06-11T09:44:14.970 回答
2

你不需要Linq。我不觉得

if(item.States.Contains(State.Important) && item.States.Contains(State.Updated))
  string toProcess = "Do";

http://msdn.microsoft.com/en-us/library/bhkz42b3.aspx

于 2013-06-11T09:43:59.617 回答
1

List 有一个Contains方法,所以你的代码是

if(item.States.Contains(State.Important) && item.States.Contains(State.Updated))
    string toProcess = "Do";

我认为在这里使用 Linq 或 lambda 表达式没有真正的好处......

于 2013-06-11T09:44:23.430 回答
0

某种以下实现

Content obj = new Content();
obj.States = SomeMethod(); 

if(obj.States.Any(h => h == State.Important) && obj.States.Any(h => h == State.Updated))
{
   string toProcess = "Do";
}
于 2013-06-11T09:45:57.703 回答
0
 var res = (from items in item
                       where items.States.Has(State.Important) && items.States.Has(State.Updated)
                       select new { NewProcess = "Do" }).ToList();


foreach (var result in res)
    {
        string result = result.NewProcess
    }

尝试这个

于 2013-06-11T09:46:20.057 回答
0

你可以和

!(new List<States>{State.Important, State.Updated}.Except(item.States).Any());

它并不是真的更短,但如果你有大量的状态要检查,它会更容易。

只要您想检查该项目是否具有所需的所有状态,您只需将新状态添加到第一个列表中。

于 2013-06-11T09:45:09.313 回答
0

也许您可以考虑将您的枚举用作一组标志,即您可以在没有列表的情况下组合多个状态:

[Flags]
public enum State
{
    Important = 1,
    Updated = 2,
    Deleted = 4,
    XXX = 8
    ....
}

public class Content
{
    public State MyState { get; set; }
}

if ((myContent.MyState & State.Important) == State.Important
    && (myContent.MyState & State.Updated) == State.Updated)
{
    // Important AND updated
}
于 2013-06-11T09:47:08.893 回答