前提知識(知っている方はスキップ推奨)
そもそもValueObjectって何?
ざっくりいうと、システム固有のオブジェクト(独自型)です。
値が持つビジネスルールを表現することができます。
詳しくはググってみてください。
そもそもハッシュ関数って何?
入力されたデータを"何らかの値"にして返却する関数です。
"何らかの値"を生成するときは、ある一定のルールに従って行われます。
したがって、同じ値をハッシュ関数に与えると、同じ値が返却されます。
尚、ハッシュ関数が返却する値をハッシュ値と言います。
ValueObjectが単一の値しか持たない場合
プロパティに対してGetHashCode()
をするだけでOKです。
public class Id
{
public int Value { get; set;}
// ...略
public override int GetHashCode()
{
return Value.GetHashCode();
}
// ...略
}
ValueObjectが複数の値を持つ場合
C#7以降の場合tupleを使用する
public class Name
{
public string First { get; set;}
public string Last { get; set;}
// ...略
public override int GetHashCode()
{
return (First, Last).GetHashCode();
}
// ...略
}
.NET Standard2.1以降の場合HashCode.Combine
を使用する
class Name
{
public Name(string first, string last)
{
First = first;
Last = last;
}
public string First { get; set; }
public string Last { get; set; }
// ...略
public override int GetHashCode()
{
return HashCode.Combine(First, Last);
}
// ...略
}
その他、自力でやる場合は素数をかけて排他論理和をとる
class Name
{
public Name(string first, string last)
{
First = first;
Last = last;
}
public string First { get; set; }
public string Last { get; set; }
// ...略
public override int GetHashCode()
{
return (First.GetHashCode() * 397) ^ Last.GetHashCode();
}
// ...略
}
適当にやるとどうなる?
例えば、return First.GetHashCode() ^ Last.GetHashCode();
のようにしてしまうと、
値がひっくり返っている場合に同一のハッシュ値となってしまいます。
上述のNameクラスで例えると、
「吉野 葵(よしの あおい)さん」と「葵 吉野(あおい よしの)さん」は別人です。
しかし、return First.GetHashCode() ^ Last.GetHashCode();
という実装だと
ただの排他論理和のため、
「吉野 葵(よしの あおい)さん」と「葵 吉野(あおい よしの)さん」のハッシュ値が同一となってしまいます。
説明した上記3つの例では、違うハッシュ値が返ります。
まとめ
- ValueObjectが単一の値しか持たない場合
-
return Value.GetHashCode();
でOK
-
- ValueObjectが複数の値を持つ場合
- tuppleを使う:
return (First, Last).GetHashCode();
-
HashCode.Combine
を使う:return HashCode.Combine(First, Last);
- 素数+排他論理和を使う:
return (First.GetHashCode() * 397) ^ Last.GetHashCode();
- tuppleを使う: