C# 为什么';我的Equals方法没有被调用吗?

C# 为什么';我的Equals方法没有被调用吗?,c#,tdd,mspec,C#,Tdd,Mspec,我正在通过Kent Beck的TDD示例作为学术练习,但使用MSpec编写测试。在下面的示例中,我喜欢引入一个转折点,这样我就不能简单地死记硬背文本,我发现这样我就容易遇到我必须解决的问题,结果,我学到了更多。我相信这就是其中之一 我已经读了肯特的“钱”的例子。这是我的班级结构: 我有以下两个测试上下文: [Subject(typeof(Money), "Equality")] public class when_comparing_different_classes_for_equality

我正在通过Kent Beck的TDD示例作为学术练习,但使用MSpec编写测试。在下面的示例中,我喜欢引入一个转折点,这样我就不能简单地死记硬背文本,我发现这样我就容易遇到我必须解决的问题,结果,我学到了更多。我相信这就是其中之一

我已经读了肯特的“钱”的例子。这是我的班级结构:

我有以下两个测试上下文:

[Subject(typeof(Money), "Equality")]
public class when_comparing_different_classes_for_equality
{
    Because of = () => FiveFrancs = new Franc(5, "CHF");
    It should_equal_money_with_currency_set_to_francs = () => FiveFrancs.Equals(new Money(5, "CHF")).ShouldBeTrue();
    static Franc FiveFrancs;
}

[Subject(typeof(Franc), "multiplication")]
public class when_multiplying_a_franc_amount_by_an_integer
{
    Because of = () => FiveFrancs = new Franc(5, null);
    It should_be_ten_francs_when_multiplied_by_2 = () => FiveFrancs.Times(2).ShouldEqual(Money.Franc(10));
    It should_be_fifteen_francs_when_multiplied_by_3 = () => FiveFrancs.Times(3).ShouldEqual(Money.Franc(15));
    static Franc FiveFrancs;
}
Times()方法返回一个包含结果的Money类型的新对象,即对象是不可变的。上面的第一个上下文传递,表明Equals按预期工作,即它忽略对象类型,只要它们都是从Money继承的,并且只比较amount和currency字段是否相等。第二个上下文失败,输出如下:

Machine.Specifications.SpecificationException
  Expected: TDDByExample.Money.Specifications.Franc:[15]
  But was:  TDDByExample.Money.Specifications.Money:[15]
   at TDDByExample.Money.Specifications.when_multiplying_a_franc_amount_by_an_integer.<.ctor>b__2() in MoneySpecs.cs: line 29

完全实现平等是这样的。看看是否有帮助

protected bool Equals(Money other)
{
    // maybe you want this extra param to Equals?
    // StringComparison.InvariantCulture
    return amount == other.amount 
      && string.Equals(currency, other.currency);
}

public override bool Equals(object obj)
{
    if (ReferenceEquals(null, obj)) return false;
    if (ReferenceEquals(this, obj)) return true;
    var other = obj as Money;
    return other != null && Equals(other);
}

public override int GetHashCode()
{
    unchecked
    {
        return (amount * 997) ^ currency.GetHashCode();
    }
}

public static bool operator ==(Money left, Money right)
{
    return Equals(left, right);
}

public static bool operator !=(Money left, Money right)
{
    return !Equals(left, right);
}

正如@Harrison所评论的,问题在于
Franc
类的
Times
方法的结果类型。它没有通过测试规范,因为它返回一个
Money
对象,但是规范需要一个
Franc
实例。更改规范以要求使用
Money
对象,或者重写
Times
方法以返回
Franc
实例

更新

更新测试规范后,您更改了以下行:

It should_be_ten_francs_when_multiplied_by_2 = () => FiveFrancs.Times(2).ShouldEqual(Money.Franc(10));
It should_be_fifteen_francs_when_multiplied_by_3 = () => FiveFrancs.Times(3).ShouldEqual(Money.Franc(15));
但是,在属性上,主体的类型仍然是:

[Subject(typeof(Franc), "multiplication")]

因此,我认为它仍然在期待一个
法郎
实例,而不是
金钱
实例。

抱歉,在过去10分钟内,我已经更新了3次这个问题,我没想到会有这么快的回答!在我更新后,你的答案仍然正确吗?[Subject]属性仅用于文档,对代码没有任何影响。这似乎有点奇怪,但这个例子是中期重构,它正朝着取消法郎和美元类的方向发展,转而支持单一的通用货币类。因此,这是一种人为的情况。我可以直接切入主题,将完成的重构代码放在那里,但我想了解这是怎么出错的。我仍然不明白为什么它不起作用。我不太确定
[Subject]
属性,但我能看到的唯一区别是对象的类型。您是否尝试过将
[Subject]
属性更改为使用
Money
类型?我尝试过更改属性,但没有效果。我100%确信它除了用于文档之外没有任何效果(事实上,您可以完全忽略它)。然而,我认为我更接近解决办法。我注意到静态字段被声明为法郎而不是货币。在将其改为货币后,测试仍然失败,但我现在有了一个更清楚的问题指示:预期:TDDByExample.Money.Specifications.Franc:[15瑞士法郎]但是was:TDDByExample.Money.Specifications.Money:[15]@TimLong:oops。法郎构造器应该是这样的,不是吗<代码>公共法郎(国际金额):基数(金额,“瑞士法郎”){}?:-)只有当
金额
货币
字段为只读(或保证永不更改)时,这种定义才正确。要求从
GetHashCode
返回的值在实例的生命周期内是不可变的。@Enigmativity:事实上,正如上面问题中所述:“即,对象是不可变的”虽然这是一个非常彻底和完整的等式实现,但它并没有回答OP问题,“为什么它一开始就没有被调用。@spender-只是提供了清晰的信息。不变性并没有得到很好的宣传,我看到很多人通过mutable
GetHashCode
实现来破坏代码。经过深思熟虑,我相信这可能是MSpec的问题。MSpec比较对象类型,并根据不相同的类型使比较失败。只有当类型相等时,MSpec才会继续使用基于值的比较。这就是为什么我的Equals方法从未被调用。然而,我认为利斯科夫说,如果我对平等的定义允许的话,我应该能够将货币与法郎进行比较。因此,我提出了一个关于MSpec项目的问题@Anthony我更喜欢Whitesmiths样式的缩进(大括号缩进)。我并不特别介意你改变我的缩进,我很乐意让你的编辑保持不变,但另一方面,无视别人的偏好似乎有点自以为是。有什么我不知道的指导原则吗?哦,对不起,我只是假设你的空格/制表符组合不好之类的。我对SO.PS的一般错误代码块格式非常满意。你可能是地球上唯一一个用白钻式牙套做C#的人,然后,你的个人资料照片中也戴着蝴蝶结,所以你看起来是一个独特的角色:D“蝴蝶结很酷,你知道的!”(第十二位医生)。早在80年代,我就在大学里学会了缩进的风格,这是我发现很难改掉的习惯之一。我的编译器编写讲师声称,既然大括号定义了一个块,那么它们就是块的一部分,应该与块一起缩进。他在那一点上很清楚,我们不敢不同意;-)
[Subject(typeof(Franc), "multiplication")]