C#返回类型中的值类型而不是基础数量

时间:2014-06-25 00:00:48

标签: c#

我正在创建一个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);
}

使用此代码,如果我写了一个失败的测试而不是获得预期和实际数量的失败消息,我得到:

  

消息:Assert.AreEqual失败。预期:< MyTypes.Utilities.Volume>。实际:< 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);
}

这是我第一次尝试这样做,所以我不确定它为什么会这样做。任何指导都表示赞赏。

4 个答案:

答案 0 :(得分:3)

第一种方法是替换

Assert.AreEqual(value, volume);

with:

Assert.AreEqual((Volume)value, volume);

另一种方法是将assert字符串替换为:

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字段来覆盖GetHashCodeamount方法只读:

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

答案 1 :(得分:1)

要查看测试打印时Volume类所包含的十进制值,只需实现ToString()的覆盖:

public override String ToString(){
    return amount.ToString();
}

答案 2 :(得分:1)

Assert.AreEqual调用object.Equals,就像这样称呼。首先,它将2个对象与object.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#中,所有值(struct)类型都继承自ValueType,它会覆盖Object Equals方法。首先它比较类型,在您的情况下是Volumedecimal,因此Equals返回false。对于相同类型,它通过反射比较字段值。因此,如果您希望Assert.AreEqualVolumedecimal进行比较,则您必须覆盖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))进行测试。

答案 3 :(得分:0)

问题在于Assert.AreEqual命令。虽然我无法直接说明实现的工作原理(documentation),但我相信该函数正在使用运算符==

为了使您的卷与Assert.AreEqual一起正常工作,您需要定义 运算符==!=

编辑:找到表明Assert.AreEqual实施IEquatable的文档 见here

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)