Class 等于 if on one 属性

Class equal with if on one property

我有一个 class 用于描述 XYZ 坐标以及 3 个属性。

class 看起来像这样:

class dwePoint
{
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }
        public string Prop1 { get; set; }
        public string Prop2 { get; set; }
        public string Prop3 { get; set; }

        public override bool Equals(object obj)
        {
            return Equals(obj as dwePoint);
        }

        protected bool Equals(dwePoint other)
        { //This doesnt seem to work
            if(Prop1== "Keep")
            {
                return false;
            }
            return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                var hashCode = X.GetHashCode();
                hashCode = (hashCode * 397) ^ Y.GetHashCode();
                hashCode = (hashCode * 397) ^ Prop1.GetHashCode();
                hashCode = (hashCode * 397) ^ Z.GetHashCode();
                return hashCode;
            }
        }
}

检查Equals上的XYZ,我可以只根据实际坐标过滤掉重复项,忽略属性。 在我的代码中,我使用了一个列表,所以我调用 List.Distinct()

现在有一件事我还想不通: 可能有 2 个点具有相同的 XYZ,但具有不同的属性。 在那种情况下,我总是想保留具有特定字符串的那个(例如 "Keep"),并始终删除具有其他值的那个。

我已经在尝试一些 if 语句,但运气不佳...

我该如何处理?

Distinct 你想要的东西实际上是不可能的,因为它使用你的 Equals 因为它只是平等的输入(它应该),所以它甚至没有办法意识到那里可能是对象之间的差异。

我认为使用新的 class 编写 class 对您来说是更好的设计,例如Point3D 包含您的坐标和您的 3 个属性。然后你可以按点分组,对于所有有一个以上相等点的东西,应用你自己的逻辑来保留哪些。

在代码中:

class Point3D
{
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }
    // Equals and get hash code here
}

class dwePoint
{
    Point3D Coordinate {get;}
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
    public string Prop3 { get; set; }
}

// Filter list by applying grouping and your custom logic
points = points.GroupBy(p => p.Coordinate)
    .Select(x => 
        x.OrderByDescending(p => p.Prop1 == "Keep")  // Sort so the element you want to keep is first
         .First()                                    // If there is only one element, the ordering will not matter
    ).ToList();

如果您确实需要,GroupBy 也适用于您当前的 class 设计,因为只有坐标参与 Equals

我的想法与 driis 提供的答案相同.. 但是,我想为您提供另一种选择。

你可以自己写一个Distinct作为扩展方法。 这是一个解决方法。我写了个示例代码,方便大家理解。

static class Program
{
    static void Main(string[] args)
    {
        List<Abc> list = new List<Abc>()
        {
            new Abc()
            {
                a = 5,
                b = 6,
                s = "Phew"
            },
            new Abc()
            {
                a = 9,
                b = 10,
                s = "Phew"
            },
            new Abc()
            {
                a = 5,
                b = 6,
                s = "Keep"
            },
            new Abc()
            {
                a = 9,
                b = 10,
                s = "Keep"
            },
            new Abc()
            {
                a = 5,
                b = 6,
                s = "Phew"
            },
            new Abc()
            {
                a = 9,
                b = 10,
                s = "Phew"
            },
        };
        list = list.MyDistinct();
    }

    // Extension Method
    public static List<Abc> MyDistinct(this List<Abc> list)
    {
        List<Abc> newList = new List<Abc>();
        foreach (Abc item in list)
        {
            Abc found = newList.FirstOrDefault(x => x.Equals(item));
            if (found == null)
            {
                newList.Add(item);
            }
            else
            {
                if (found.s != "Keep" && item.s == "Keep")
                {
                    newList.Remove(found);
                    newList.Add(item);
                }
            }
        }
        return newList;
    }
}

class Abc
{
    public int a, b;
    public string s;

    public override bool Equals(object obj)
    {
        Abc other = obj as Abc;
        return a == other.a && b == other.b;
    }

    public override int GetHashCode()
    {
        return a.GetHashCode() ^ b.GetHashCode();
    }
}

希望对你有所帮助..

如果 Prop1 == null,您的 GetHashCode 会遇到 nullref,您应该解决这个问题。

另一种解决方案:使用聚合和 Lamda 来区分您的列表。 class 上的 Equal() 仅比较 X、Y 和 Z - 聚合 lambda 可确保您保留所需内容。可能将聚合放在扩展方法或函数中。

static void Main()
{
    List<dwePoint> points = new List<dwePoint>();
    // Testdata
    for (int x = 0; x < 3; x++)
        for (int y = 0; y < 3; y++)
            for (int z = 0; z < 3; z++)
            {
                points.Add(new dwePoint { X = x, Y = y, Z = z });
                if (x == y && x == z) // and some duplicates to Keep
                    points.Add(new dwePoint { X = x, Y = y, Z = z, Prop1 = "Keep" });
            }

    // prefer the ones with "Keep" in Prop1  
    var distincts = points.Aggregate(new HashSet<dwePoint>(), (acc, p) =>
    {
        if (acc.Contains(p))
        {
            var oldP = acc.First(point => point.X == p.X && point.Y == p.Y && point.Z == p.Z);
            if (oldP.Prop1 == "Keep")
            {
                // do nothing - error, second point with "keep"
            }
            else
            {
                acc.Remove(oldP);
                acc.Add(p); // to use this ones other props later on ....
            }
        }
        else
            acc.Add(p);

        return acc;
    }).ToList();

    Console.WriteLine(string.Join(" - ", points));
    Console.WriteLine(string.Join(" - ", distincts));
    Console.ReadLine();
}


private class dwePoint
{
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
    public string Prop3 { get; set; }
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }

    public override bool Equals(object obj)
    {
        return Equals(obj as dwePoint);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            var hashCode = X.GetHashCode();
            hashCode = (hashCode * 397) ^ Y.GetHashCode();
            hashCode = (hashCode * 397) ^ Z.GetHashCode();
            return hashCode;
        }
    }

    public override string ToString() => $"{X}-{Y}-{Z}-{Prop1}-{Prop2}-{Prop3}";

    protected bool Equals(dwePoint other)
    {
        return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
    }
}