c#返回类型中的值类型,而不是底层数量
本文关键字:返回类型 类型 | 更新日期: 2023-09-27 18:10:19
我正在创建一个Volume值类型,到目前为止还不错,但是当我覆盖乘法运算符并编写单元测试时,如果测试失败而不是获得预期和实际数量,我将获得完整的合格类型名称。
代码中没有太多内容:
private decimal amount;
public Volume(decimal value)
{
amount = value;
}
public static implicit operator Volume(decimal value)
{
return new Volume(value);
}
//... continue same methods with all number types
public static Volume operator *(Volume left, decimal right)
{
return new Volume(left.amount * right);
}
使用这段代码,如果我写一个失败的测试,而不是得到预期的和实际数量的失败消息,我得到:
信息:断言。AreEqual失败了。预期:& lt; MyTypes.Utilities.Volume>。实际:& lt; MyTypes.Utilities.Volume>.
我尝试添加以下内容:
public static implicit operator Decimal(Volume value)
{
return value.amount;
}
这不仅不起作用,而且现在证明该类型可以用十进制数初始化的测试失败了,出现了同样的消息:
[TestMethod]
public void VolumeTypeGetsInitializedByDecimalValue()
{
Decimal value = 123456781.1235657789464356m;
Volume volume = value;
Assert.AreEqual(value, volume);
}
这是我第一次尝试这样做,所以我不确定为什么它的行为方式。
第一种方法是替换
Assert.AreEqual(value, volume);
:
Assert.AreEqual((Volume)value, volume);
另一种方法是用:
Assert.IsTrue(value.Equals(volume), string.Format("It was expected to get '{0}' but got '{1}'.", volume, value));
和重写ToString:
public override String ToString(){
return amount.ToString();
}
为了使事情"正确",除了覆盖ToString
方法外,我还建议覆盖Equals
和GetHashCode
方法,将amount
字段标记为只读:
private readonly decimal amount;
public bool Equals(Volume other)
{
return amount == other.amount;
}
public override bool Equals(object obj)
{
return obj is Volume ? Equals((Volume)obj) : base.Equals(obj);
}
public override int GetHashCode()
{
return amount.GetHashCode();
}
要在测试打印时看到卷类所包含的十进制值,只需实现ToString()
的覆盖:
public override String ToString(){
return amount.ToString();
}
Assert.AreEqual
调用object.Equals
,实现如下。首先,它将两个对象与两个对象进行比较。ReferenceEquals,如果它们不相等,则检查其中一个是否为空,如果不是,则调用其中一个对象的Equals
方法。
static bool Equals(object a, object b)
{
if(ReferenceEquals(a, b)
return true;
if(a == null || b == null)
return false;
return a.Equals(b);
}
c#中所有的value (struct)类型继承自ValueType
,它覆盖了Object
Equals方法。首先比较类型,在您的示例中是Volume
和decimal
,因此Equals
返回false。对于相等类型,它通过反射比较字段值。所以,如果你想让Assert.AreEqual
比较Volume
和decimal
,你必须重写Equals
,像这样:
public override bool Equals(object obj)
{
if (obj is decimal)
{
return amount == (decimal) obj;
}
if (obj is Volume)
{
return amount == ((Volume) obj).amount;
}
return false;
}
仍然有可能Assert.AreEqual(Decimal, Volume)
和Assert.AreEqual(Volume, Decimal)
会产生不同的结果,所以我建议你用Assert.IsTrue(Volume.Equals(Decimal))
测试。
问题出在Assert.AreEqual
命令上。虽然我不能直接说明实现是如何工作的(文档),但我相信该函数使用了操作符==
。
为了使您的卷与Assert.AreEqual
正确工作,您需要定义操作符==
和!=
.
EDIT:发现Assert.AreEqual
实现IEquatable
的文档看到
public static bool operator !=(Volume valA, Volume valB)
{
return valA.amount != valB.amount;
}
public static bool operator ==(Volume valA, Volume valB)
{
return valA.amount == valB.amount;
}
注意:您还需要实现Equals &GetHashCode(或使用IEquatable)