C# 如何在具有不同值的列表中查找相同的类

C# 如何在具有不同值的列表中查找相同的类,c#,linq,C#,Linq,我有一份特权清单。特权类有四个属性:类型、访问类型、值和操作。 如果存在类型、访问类型和值相同但操作不同的多个特权,我想抛出一个异常 例如,p1和p2的列表将引发异常: Privilege p1 = new Privilege{Type = "a", AccessType = "a", Value = "a", Action = "a"}; Privilege p2 = new Privilege{Type = "a", AccessType = "a", Value = "a", Action

我有一份特权清单。特权类有四个属性:类型、访问类型、值和操作。 如果存在类型、访问类型和值相同但操作不同的多个特权,我想抛出一个异常

例如,p1和p2的列表将引发异常:

Privilege p1 = new Privilege{Type = "a", AccessType = "a", Value = "a", Action = "a"};
Privilege p2 = new Privilege{Type = "a", AccessType = "a", Value = "a", Action = "b"};

我想使用LINQ,但不确定如何使用。

所以您希望允许重复类型+访问类型+值,但前提是操作也相同

bool throwException = pList
    .GroupBy(x => new { x.Type, x.AccessType, x.Value })
    .Any(g => g.Select(p => p.Action).Distinct().Count() > 1);
首先,我将构建这三个属性的组。然后我检查这些组中是否有任何一个包含多个操作。然后可以抛出异常

如果替换,可能会出现一个小优化(如果重复列表很大)

Distinct().Count() > 1


听起来您想按
类型
访问类型
(可以使用匿名类型)进行分组,然后检查操作。假设在每种情况下,
操作
的值相同,则可以编写如下代码:

// Normally I'd expect you to have some kind of sequence of privileges rather
// than separate variables.
var privileges = new[] { p1, p2 };
var failures = privileges
    // Group the privileges by 3 properties, extracting actions as the values
    .GroupBy(p => new { p.Type, p.AccessType, p.Value }, p => p.Action)
    // Find any groups which have more than one distinct action
    .Where(g => g.Distinct().Skip(1).Any())
    .ToList();

if (failures.Count > 0)
{
    // There's at least one failure. There may be many. Throw
    // whatever exception you want here. This is just an example.
    var individualMessages =
        failures.Select(g => $"{g.Key}: {string.Join(", ", g.Distinct())}");
    throw new Exception(
        $"Invalid privilege combinations: {string.Join("; ", individualMessages})");
}
下面是一个完整的示例,显示了几个故障:

using System;
using System.Collections.Generic;
using System.Linq;

class Privilege
{
    public string Type { get; }
    public string AccessType { get; }
    public string Value { get; }
    public string Action { get; }

    public Privilege(string type, string accessType, string value, string action)
    {
        Type = type;
        AccessType = accessType;
        Value = value;
        Action = action;
    }
}

class Test
{
    static void Main()
    {
        var privileges = new List<Privilege>
        {
            // Bad: should cause an exception
            new Privilege("a", "a", "a", "a"),
            new Privilege("a", "a", "a", "b"),

            // Another bad one; let's check the
            // failure represents both
            new Privilege("x", "y", "z", "action1"),
            new Privilege("x", "y", "z", "action2"),

            // These have one property difference
            // in each case
            new Privilege("b", "a", "a", "a"),
            new Privilege("a", "b", "a", "a"),
            new Privilege("a", "a", "b", "a"),

            // Duplicate type/access/value, but same action too.
            new Privilege("d", "e", "f", "action"),
            new Privilege("d", "e", "f", "action")
        };
        CheckPrivileges(privileges);
    }

    static void CheckPrivileges(IEnumerable<Privilege> privileges)
    {
        var failures = privileges
            // Group the privileges by 3 properties, extracting actions as the values
            .GroupBy(p => new { p.Type, p.AccessType, p.Value }, p => p.Action)
            // Find any groups which have more than one distinct action
            .Where(g => g.Distinct().Skip(1).Any())
            .ToList();

        if (failures.Count > 0)
        {
            // There's at least one failure. There may be many. Throw
            // whatever exception you want here. This is just an example.
            var individualMessages =
                failures.Select(g => $"{g.Key}: {string.Join(", ", g.Distinct())}");
            throw new Exception(
                $"Invalid privilege combinations: {string.Join("; ", individualMessages)}");
        }
    }
}

如果重复的
计数大于0,则可以抛出异常。

我认为这里最简单的解决方案是使用2
Any()
s



为什么要使用LINQ而不重写Equals?这实际上不会引发异常,但您可以执行以下操作:if(privileges.Contains(x=>!(x.Type==p1.Type&&x.AccessType..){privileges.Add(p1);}它不会扔,但你不会让箱子掉下来happen@AsfKEquals已被重写,我将其用于另一个问题。这是不正确的。OP需要重复项,但这些重复项必须具有相同的操作。不,他说
,但操作不同。
。确切地说,应该引发异常,因为操作不同。I以与Tim相同的方式阅读此内容:如果操作相同,那没关系。(请参阅我的答案以获取显示此内容的完整示例。)是的,只有当操作不同时才例外。“如果存在类型、访问类型和值相同但操作不同的多个特权,我想引发异常”@弗洛里安:谢谢你的提琴,你说得对。我现在已经重新做了:有没有理由单独使用
Select
,而不是使用
GroupBy
重载来选择
p.Action
?(以我的回答为例)@DaisyShipton:你的方法非常好。事实上,我总是忘记了
GroupBy
重载,因为你总是可以在没有:)的情况下实现同样的效果。这包括使用g.Select(p=>p.Action.Distinct().Count()每次迭代访问整个列表。对于一个简单的问题来说,这也是一个非常复杂的方法。@Chriz:不,
Distinct
只枚举当前的
操作
-组,而
Count
只计算此组的重复操作。如果没有异常状态,您甚至会为其中的每个特权枚举一次列表,但您正在抱怨我的错误GroupBy方法:)@TimSchmelter我想我们必须做一个性能测试,在哪种情况下哪个解决方案更快:)。太多的努力换来的结果太少。请随意建立大样本列表。但是认为抛出异常是一种非常罕见的例外状态(如果有的话)。所以你不能这么想。正常状态是,大列表不会包含每个组的不同操作。在这种情况下,您将检查每个特权,并再次将每个特权与其他特权进行比较。@TimSchmelter您是对的,“ExceptionalState”起作用。在一个正常的成功案例中,你需要每次检查所有元素,而早期元素的可能命中的好处就消失了。即使如此,我也不需要每次检查“所有元素”。我还使用了
Any
,因此如果第一个组已经包含无效状态,它将立即停止处理。此外,
GroupBy
Distinct
都是使用基于集合的方法实现的,并且非常有效。
using System;
using System.Collections.Generic;
using System.Linq;

class Privilege
{
    public string Type { get; }
    public string AccessType { get; }
    public string Value { get; }
    public string Action { get; }

    public Privilege(string type, string accessType, string value, string action)
    {
        Type = type;
        AccessType = accessType;
        Value = value;
        Action = action;
    }
}

class Test
{
    static void Main()
    {
        var privileges = new List<Privilege>
        {
            // Bad: should cause an exception
            new Privilege("a", "a", "a", "a"),
            new Privilege("a", "a", "a", "b"),

            // Another bad one; let's check the
            // failure represents both
            new Privilege("x", "y", "z", "action1"),
            new Privilege("x", "y", "z", "action2"),

            // These have one property difference
            // in each case
            new Privilege("b", "a", "a", "a"),
            new Privilege("a", "b", "a", "a"),
            new Privilege("a", "a", "b", "a"),

            // Duplicate type/access/value, but same action too.
            new Privilege("d", "e", "f", "action"),
            new Privilege("d", "e", "f", "action")
        };
        CheckPrivileges(privileges);
    }

    static void CheckPrivileges(IEnumerable<Privilege> privileges)
    {
        var failures = privileges
            // Group the privileges by 3 properties, extracting actions as the values
            .GroupBy(p => new { p.Type, p.AccessType, p.Value }, p => p.Action)
            // Find any groups which have more than one distinct action
            .Where(g => g.Distinct().Skip(1).Any())
            .ToList();

        if (failures.Count > 0)
        {
            // There's at least one failure. There may be many. Throw
            // whatever exception you want here. This is just an example.
            var individualMessages =
                failures.Select(g => $"{g.Key}: {string.Join(", ", g.Distinct())}");
            throw new Exception(
                $"Invalid privilege combinations: {string.Join("; ", individualMessages)}");
        }
    }
}
Unhandled Exception: System.Exception: Invalid privilege combinations:
{ Type = a, AccessType = a, Value = a }: a, b;
{ Type = x, AccessType = y, Value = z }: action1, action2
var duplicates = privilegecollection.GroupBy(x => new {x.Type, x.AccessType, x.Value})
                                    .Where(g => g.Count() > 1)
                                    .Select(g => g.Key);
bool HasDoubleOtherAction = privilegeList.Any(outer => privilegeList
                               .Any(inner => outer.Type == inner.Type
                                             && outer.AccessType == inner.AccessType
                                             && outer.Value == inner.Value
                                             && outer.Action != inner.Action));