在字典中获取重复的接口条目
Getting duplicated entries of interfaces in a Dictionary
所以我有一个名为 IResource
的接口,它由这 5 个只读属性组成,但由于我将它们添加到 Dictionary<IResource, int>
我需要一种方法来比较两个 IResource
所以我的词典中没有重复项。有没有办法为每个 IResource 添加一个默认的 Equals(object obj)
?
我已经在 Wood
class 中添加了 Equals
覆盖,它解决了问题,但我必须在每个 class 中添加一个 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
界面时,它总是会添加一个新条目。
您可以将比较移动到基础 class 并在那里覆盖 Equals
和 GetHashCode
。只需将您想要在比较中使用的任何成员添加到摘要中 class 并将它们包含在股权比较中。
例如:
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
}
您可以创建实现 IResource
的 abstract
class。使用 class 并覆盖 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;
}
这将为您提供预期的行为。
虽然创建抽象基础 class 当然是可能的 - 正如其他人所指出的 - 这确实不是一个好主意。您正在创建一个依赖项,任何实现 IResource 的 class 也必须实现您为 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 class 并创建了另一个名为 Metal 的。两者都不必共享基数 class 或覆盖 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 风格金属 class:
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;
}
所以我有一个名为 IResource
的接口,它由这 5 个只读属性组成,但由于我将它们添加到 Dictionary<IResource, int>
我需要一种方法来比较两个 IResource
所以我的词典中没有重复项。有没有办法为每个 IResource 添加一个默认的 Equals(object obj)
?
我已经在 Wood
class 中添加了 Equals
覆盖,它解决了问题,但我必须在每个 class 中添加一个 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
界面时,它总是会添加一个新条目。
您可以将比较移动到基础 class 并在那里覆盖 Equals
和 GetHashCode
。只需将您想要在比较中使用的任何成员添加到摘要中 class 并将它们包含在股权比较中。
例如:
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
}
您可以创建实现 IResource
的 abstract
class。使用 class 并覆盖 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;
}
这将为您提供预期的行为。
虽然创建抽象基础 class 当然是可能的 - 正如其他人所指出的 - 这确实不是一个好主意。您正在创建一个依赖项,任何实现 IResource 的 class 也必须实现您为 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 class 并创建了另一个名为 Metal 的。两者都不必共享基数 class 或覆盖 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 风格金属 class:
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;
}