我有一个枚举属性列表的项目的集合。
原始财产看起来像

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做到这一点?

最佳答案

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

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


否则,就像@ElRonnoco一样使用Contains()

(但是,如果您的状态是标志(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))

10-04 23:03
查看更多