2009-04-27 89 views
4

我已經定義了一個C#類的字符串成員。對於所有意圖的目的,可以將此類視爲字符串的子類(除非不允許)。我用它來表示與特定格式相匹配的強類型字符串字段(我已經大幅簡化了這一點)。覆蓋等於和比較字符串

public class field 
{ 
    private readonly string m_field; 
    public field(string init_value) 
    { 
     //Check the syntax for errors 
     if (CheckSyntax(init_value)) 
     { 
      m_field = init_value; 
     } 
     else 
     { 
      throw new ArgumentOutOfRangeException(); 
     } 
    } 

    public override string ToString() 
    { 
     return m_field; 
    } 
} 

現在,我想能夠直接比較這個類與任何其他字符串(對象或文字)。所以,我實現了在類以下內容:

public override bool Equals(object obj) 
{ 
    if (obj == null) 
    { 
     return false; 
    } 

    return this.m_field == obj.ToString(); 
} 

public override int GetHashCode() 
{ 
    return this.m_field.GetHashCode(); 
} 

public static bool operator ==(field x, Object y) 
{ 
    if ((object)x == null && y == null) 
    { 
     return true; 
    } 
    else if ((object)x == null || y == null) 
    { 
     return false; 
    } 
    else 
    { 
     return (x.m_field == y.ToString()); 
    } 
} 

public static bool operator !=(field x, Object y) 
{ 
    return !(x == y); 
} 

現在,當我寫一個單元測試,具體取決於我傳遞的參數Assert.AreEqual的順序上,我得到不同的結果:

string valid = "Some String"; 
field target = new field(valid); 
Assert.AreEqual(target, valid); // PASSES 
Assert.AreEqual(valid, target); // FAILS 

我假設這是因爲在第一個斷言,它調用field.Equals()和在第二個它調用String.Equals()。很明顯,我從錯誤的角度來看待這個問題。任何人都可以給我一些見解嗎?

另一件事。我不能在這裏使用結構(值類型),因爲在我的實際情況中,我將所有這些定義在基類中並從中繼承。

回答

5

對此進行了詳細的Effective Java如第8項說明。

equals方法實現等價關係。

這是自反的,對稱的,傳遞的,一致的,對於任何非空引用X,x.equals(null)必須返回false。引用來破壞對稱性的例子與你的類似。

field類是知道string類,但內置的string類不知道field。這是一種單向互操作性,應該刪除。

9

基本上你不能做你想做的事 - 你無法讓string認可你的班級是爲了平等的目的。你永遠無法反思 - 你永遠無法遵守object.Equals的合同。

我會親自嘗試重新設計它,以便您沒有將驗證作爲類型本身的一部分 - 使其成爲業務實體(或其他任何類型)的相關屬性的一部分。覆蓋equals當滿足一般的合同:

+0

+1像這樣的類型使用起來很棘手,而且要讓其他人一致地使用它們會很棘手。在實體本身中保留驗證和其他業務邏輯更好。 – 2009-04-27 00:34:40

+0

不幸的是我使用這個類的實例作爲字典和集合等中的鍵。我需要Equals()和GetHashCode()的行爲類似值類型,以便他們在這些情況下正確工作。 – 2009-04-27 01:10:38

+0

覆蓋Equals(),你可能想考慮實現IComparable http://msdn.microsoft.com/en-us/library/system.icomparable.aspx 我真的希望MS會把操作符重載出c#。 – 2009-04-27 01:32:00

4

我不鼓勵使用你的領域類隱式爲String的人,並迫使該類型的使用:

string valid = "Some String"; 
field target = new field(valid); 
Assert.AreEqual(target.toString(), valid); 
Assert.AreEqual(valid, target.toString()); 
0

根據大家的反饋,以及我自己的需要,這裏就是我提出作爲一個可能的解決方案(我修改Equals方法如下):

public override bool Equals(Object obj) 
{ 
    if (obj == null) 
    { 
     return false; 
    } 

    field f = obj as field; 
    if (f != null) 
    { 
     return this == f; 
    } 
    else 
    { 
     return obj.Equals(this); 
    } 
} 

這似乎允許其正確用於依賴Equals和GetHashCode方法確定值是否已經存在的字典和集合類。

而且,現在這些都失敗:

string valid = "Some String"; 
field target = new field(valid); 
Assert.AreEqual(target, valid); // FAILS 
Assert.AreEqual(valid, target); // FAILS 

而這些都傳遞:

string valid = "Some String"; 
field target = new field(valid); 
Assert.AreEqual(target.ToString(), valid); // PASSES 
Assert.AreEqual(valid, target.ToString()); // PASSES 

而這些都傳遞:

field f1 = new field("Some String"); 
field f2 = new field("Some String"); 
Assert.AreEqual(f1, f2); // PASSES 
Assert.AreEqual(f2, f1); // PASSES 
0

這是字符串#的Equals

public override bool Equals(object obj) 
{ 
    string strB = obj as string; 
    if ((strB == null) && (this != null)) 
    { 
     return false; 
    } 
    return EqualsHelper(this, strB); 
} 

將字符串以外的參數提供給字符串#等於將返回false。我會建議一個「重新思考」來解決這個問題。

0

如果您在內部嘗試驗證x或y是否爲空,我建議您使用object.ReferenceEquals()

public static bool operator ==(field x, Object y) 
{ 
    if (object.ReferenceEquals(x, null) && object.ReferenceEquals(y, null)) 
    { 
     return true; 
    } 
    else if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null)) 
    { 
     return false; 
    } 
    else 
    { 
     return (x.m_field == y.ToString()); 
    } 
}