获取字典中接口的重复条目

问题描述 投票:0回答:3

我有一个名为

IResource
的接口,由 5 个只读属性组成。 由于我将它们添加到
Dictionary<IResource, int>
,因此我需要一种方法来比较两个
IResource
值,这样字典中就不会出现重复项。

有没有办法为每个

Equals(object obj)
添加默认的
IResource

我已经在

Equals
类中添加了
Wood
覆盖,它解决了问题,但我必须在实现
Equals(object obj)
的每个类中添加
IResource

public class Wood : IResource
{
    public string Package => "Core";

    public string Family => "Wood";

    public string Name => "Wood";

    public bool IsFractal => false;

    public ResourceType Type => ResourceType.Natural;
}

PS:我对字典覆盖了

Add(IResource key, uint value)
方法来检查
IResource
是否已经存在。

    public new void Add(IResource key, uint value)
    {
        if (base.ContainsKey(key))
            base[key] += value;
        else
            base.Add(key, value);
    }

现在,当我向字典添加

IResource
界面时,它总是会添加一个新条目。

c# inheritance interface overriding equality
3个回答
2
投票

您可以将比较移至基类并覆盖那里的

Equals
GetHashCode
。只需将您想要在比较中使用的任何成员添加到抽象类,并将它们包含在权益比较中即可。

例如:

public enum ResourceType { Natural }

public interface IResource
{
    public string Name { get; }
    public ResourceType ResourceType { get; }
}

public abstract class Resource
{
    public abstract string Name { get; }
    public abstract ResourceType ResourceType { get; }
    // other properties that you want to use for your resource comparision

    public override bool Equals(object obj)
        => obj is Resource r && Name == r.Name && ResourceType == r.ResourceType;

    public override int GetHashCode() => (Name, ResourceType).GetHashCode();
}

public class Wood : Resource, IResource
{
    public override string Name => "Wood";
    public override ResourceType ResourceType => ResourceType.Natural;
    // ... other properties
}

1
投票

虽然创建一个抽象基类当然是可能的——正如其他人指出的那样——但这确实不是一个好主意。 您正在创建一个依赖项,任何实现 IResource 的类也必须实现您为 IResource 定义的相等性。这可能没问题,也可能会导致维护变得困难并导致错误。

该框架旨在通过允许您自定义字典进行比较的方式来处理这种情况。 它通过使用 IEqualityComparer 来做到这一点。

这是 IResource 接口的示例:

public class ResourceComparer : IEqualityComparer<IResource>
{
    public bool Equals([AllowNull] IResource x, [AllowNull] IResource y)
    {
        if (null == x && null == y)
            return true;

        if (null == x || null == y)
            return false;

        return x.Package.Equals(y.Package) &&
            x.Family.Equals(y.Family) &&
            x.Name.Equals(y.Name) &&
            x.IsFractal.Equals(y.IsFractal) &&
            x.Type.Equals(y.Type);
    }

    public int GetHashCode([DisallowNull] IResource obj)
    {
        HashCode hash = new HashCode();
        hash.Add(obj.Package);
        hash.Add(obj.Family);
        hash.Add(obj.Name);
        hash.Add(obj.IsFractal);
        hash.Add(obj.Type);
        return hash.ToHashCode();
    }
}

一旦获得,您就可以使用该比较器创建字典。我使用了您的 Wood 类并创建了另一个名为 Metal 的类。 两者都不必共享基类或重写 Equals 和 GetHashCode。

    static void Main(string[] _)
    {
        var resourceMap = new Dictionary<IResource,uint>(new ResourceComparer());

        var resources = new IResource[] { new Wood(), new Metal(), 
                                         new Wood(), new Wood() };

        foreach (var r in resources)
        {
            if (resourceMap.TryGetValue(r, out var count))
                resourceMap[r] = count + 1;
            else
                resourceMap.Add(r, 1);
        }

        Console.WriteLine(resourceMap[new Wood()]);
        Console.WriteLine(resourceMap[new Metal()]);
    }

这是简单的 POCO 风格金属类:

public class Metal : IResource
{
    public string Package => "Core";

    public string Family => "Metal";

    public string Name => "Metal";

    public bool IsFractal => false;

    public ResourceType Type => ResourceType.ManMade;
}

1
投票

您可以创建一个实现

abstract
IResource
类。使用该类并覆盖
Equals
GetHashCode

public abstract class Resource : IResource
{
    //make all your interface properties abstract
    public abstract string Package { get; }
    public abstract string Family { get; }
    public abstract string Name { get; }
    public abstract bool IsFractal { get; }
    public abstract ResourceType Type { get; }

    public override bool Equals(object obj)
    {
        if (!(obj is Resource resource)) return false;
        return ReferenceEquals(this, resource) ||
               Package == resource.Package &&
               Family == resource.Family &&
               Name == resource.Family &&
               IsFractal == resource.IsFractal &&
               Type == resource.Type;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(Package, Family, Name, IsFractal, Type);
    }
}

然后让你的所有资源实施

abstract class Resource

public class Wood : Resource
{
    public override string Package => "Core";

    public override string Family => "Wood";

    public override string Name => "Wood";

    public override bool IsFractal => false;

    public override ResourceType Type => ResourceType.Natural;
}

public class Rock : Resource
{
    public override string Package => "Core";

    public override string Family => "Rock";

    public override string Name => "Rock";

    public override bool IsFractal => false;

    public override ResourceType Type => ResourceType.Natural;
}

这将为您提供您期望的行为。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.