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);
}

这是我第一次尝试这样做,所以我不确定为什么它的行为方式。

c#返回类型中的值类型,而不是底层数量

第一种方法是替换

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方法外,我还建议覆盖EqualsGetHashCode方法,将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方法。首先比较类型,在您的示例中是Volumedecimal,因此Equals返回false。对于相等类型,它通过反射比较字段值。所以,如果你想让Assert.AreEqual比较Volumedecimal,你必须重写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)