C# 集合与 NSubstitue 代理对象覆盖等于
C# collection with NSubstitue proxy objects overwrite Equals
我在将 .NET 集合与 Nsubstitue 对象一起使用时遇到问题。
- 我有一个基础 class,我在其中实现了 Equals(object)、CompareTo 函数
- 在测试中,我为此基础创建了两个精确的 Nsubstitue 对象代理 class。
- 我把对象放入集合后,集合显示这两个对象代理是两个不同的对象。
我想知道这种行为的原因是什么,以及如何使用模型定义集合。
public class KeyTestClass : IKeyTestClass
{
public int Id { get; private set; }
public KeyTestClass()
{
Id = 1;
}
public override int GetHashCode()
{
return Id;
}
public int CompareTo(IKeyTestClass other)
{
return Id - other.Id;
}
public bool Equals(IKeyTestClass other)
{
return Id == other.Id;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((KeyTestClass)obj);
}
}
public interface IKeyTestClass : IComparable<IKeyTestClass>, IEquatable<IKeyTestClass>
{
int Id { get; }
}
public class KeyTestClass2 : IKeyTestClass2
{
}
public interface IKeyTestClass2
{
}
[TestClass]
public class ConsistencyRelatedTests
{
[TestMethod]
public void ValidateTestClass()
{
var dic = new Dictionary<IKeyTestClass,List<IKeyTestClass2>>();
// using new equals function defined by Nsubstittue.
var item1 = Substitute.For<IKeyTestClass>();
var item2 = Substitute.For<IKeyTestClass>();
item1.Id.Returns(1);
item2.Id.Returns(1);
Assert.IsTrue(item1.Equals(item2)); //false, not working
dic.Add(item1, new List<IKeyTestClass2>());
dic.Add(item2, new List<IKeyTestClass2>());
// Using real class equals method
var item3 = new KeyTestClass();
var item4 = new KeyTestClass();
Assert.IsTrue(item3.Equals(item4)); //working
dic.Add(item3, new List<IKeyTestClass2>());
dic.Add(item4, new List<IKeyTestClass2>());
}
}
在此示例中,Equals()
是为 KeyTestClass
实现的。这并不意味着它适用于 IKeyTestClass
的所有实例。如果我们创建另一个 class KeyTestClass2 : IKeyTestClass
并使用默认 Equals
我们会得到完全相同的结果:
var item1 = new KeyTestClass2();
var item2 = new KeyTestClass2();
Assert.IsTrue(item1.Equals(item2)); // also fails, as expected
这几乎就是 NSubstitute 在我们调用 Substitute.For<IKeyTestClass>()
时所做的事情。它创建了一个新的 class 实现接口,因此它完全不知道 KeyTestClass.Equals
实现。
这里有几个选项,具体取决于您要执行的操作。首先,您的 KeyTestClass.Equals
适用于所有 IKeyTestClass
实例,包括替代品,因此您可以很好地使用它:
var item1 = new KeyTestClass();
var item2 = Substitute.For<IKeyTestClass>();
var item3 = Substitute.For<IKeyTestClass>();
item2.Id.Returns(1);
item3.Id.Returns(42);
Assert.IsTrue(item1.Equals(item2)); // Passes (as expected)
Assert.IsFalse(item1.Equals(item3)); // Passes (as expected)
其次,您可以用 IEquatable<IKeyTestClass>.Equals
方法替换替代品(注意:不适用于 Object.Equals
,即 can cause problems)。
var item1 = Substitute.For<IKeyTestClass>();
var item2 = Substitute.For<IKeyTestClass>();
item1.Id.Returns(1);
item2.Id.Returns(1);
item1.Equals(item2).Returns(true);
Assert.IsTrue(item1.Equals(item2)); // Passes as expected
在这种情况下,您可能还想删除 item2.Equals(item1)
。我们还可以使用 item1.Equals(Arg.Any<IKeyTestClass>).Returns(x => KeyTestClass.Equals(item1, x.Arg<IKeyTestClass>())
委托给真正的逻辑(需要提取 Equals
逻辑的 static
版本),并使用此调用配置每个替代。
要考虑的另一个选择是尽可能在此处使用真实的 KeyTestClass
(或手动编码的测试替身)。由于所有内容都回退到 Object.Equals
,平等在 .NET 中可能有点棘手,因此当平等是契约的关键部分时,需要稍微小心地消除这一点。
我在将 .NET 集合与 Nsubstitue 对象一起使用时遇到问题。
- 我有一个基础 class,我在其中实现了 Equals(object)、CompareTo 函数
- 在测试中,我为此基础创建了两个精确的 Nsubstitue 对象代理 class。
- 我把对象放入集合后,集合显示这两个对象代理是两个不同的对象。
我想知道这种行为的原因是什么,以及如何使用模型定义集合。
public class KeyTestClass : IKeyTestClass
{
public int Id { get; private set; }
public KeyTestClass()
{
Id = 1;
}
public override int GetHashCode()
{
return Id;
}
public int CompareTo(IKeyTestClass other)
{
return Id - other.Id;
}
public bool Equals(IKeyTestClass other)
{
return Id == other.Id;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((KeyTestClass)obj);
}
}
public interface IKeyTestClass : IComparable<IKeyTestClass>, IEquatable<IKeyTestClass>
{
int Id { get; }
}
public class KeyTestClass2 : IKeyTestClass2
{
}
public interface IKeyTestClass2
{
}
[TestClass]
public class ConsistencyRelatedTests
{
[TestMethod]
public void ValidateTestClass()
{
var dic = new Dictionary<IKeyTestClass,List<IKeyTestClass2>>();
// using new equals function defined by Nsubstittue.
var item1 = Substitute.For<IKeyTestClass>();
var item2 = Substitute.For<IKeyTestClass>();
item1.Id.Returns(1);
item2.Id.Returns(1);
Assert.IsTrue(item1.Equals(item2)); //false, not working
dic.Add(item1, new List<IKeyTestClass2>());
dic.Add(item2, new List<IKeyTestClass2>());
// Using real class equals method
var item3 = new KeyTestClass();
var item4 = new KeyTestClass();
Assert.IsTrue(item3.Equals(item4)); //working
dic.Add(item3, new List<IKeyTestClass2>());
dic.Add(item4, new List<IKeyTestClass2>());
}
}
在此示例中,Equals()
是为 KeyTestClass
实现的。这并不意味着它适用于 IKeyTestClass
的所有实例。如果我们创建另一个 class KeyTestClass2 : IKeyTestClass
并使用默认 Equals
我们会得到完全相同的结果:
var item1 = new KeyTestClass2();
var item2 = new KeyTestClass2();
Assert.IsTrue(item1.Equals(item2)); // also fails, as expected
这几乎就是 NSubstitute 在我们调用 Substitute.For<IKeyTestClass>()
时所做的事情。它创建了一个新的 class 实现接口,因此它完全不知道 KeyTestClass.Equals
实现。
这里有几个选项,具体取决于您要执行的操作。首先,您的 KeyTestClass.Equals
适用于所有 IKeyTestClass
实例,包括替代品,因此您可以很好地使用它:
var item1 = new KeyTestClass();
var item2 = Substitute.For<IKeyTestClass>();
var item3 = Substitute.For<IKeyTestClass>();
item2.Id.Returns(1);
item3.Id.Returns(42);
Assert.IsTrue(item1.Equals(item2)); // Passes (as expected)
Assert.IsFalse(item1.Equals(item3)); // Passes (as expected)
其次,您可以用 IEquatable<IKeyTestClass>.Equals
方法替换替代品(注意:不适用于 Object.Equals
,即 can cause problems)。
var item1 = Substitute.For<IKeyTestClass>();
var item2 = Substitute.For<IKeyTestClass>();
item1.Id.Returns(1);
item2.Id.Returns(1);
item1.Equals(item2).Returns(true);
Assert.IsTrue(item1.Equals(item2)); // Passes as expected
在这种情况下,您可能还想删除 item2.Equals(item1)
。我们还可以使用 item1.Equals(Arg.Any<IKeyTestClass>).Returns(x => KeyTestClass.Equals(item1, x.Arg<IKeyTestClass>())
委托给真正的逻辑(需要提取 Equals
逻辑的 static
版本),并使用此调用配置每个替代。
要考虑的另一个选择是尽可能在此处使用真实的 KeyTestClass
(或手动编码的测试替身)。由于所有内容都回退到 Object.Equals
,平等在 .NET 中可能有点棘手,因此当平等是契约的关键部分时,需要稍微小心地消除这一点。