私は最近、いくつかの異なる方法でこの質問をしましたが、変化するものへの参照を保持するときに Dictionary をどのよう<T,U>
に処理する必要があるかを示す答えが得られませんT.GetHashCode()
。この質問の目的上、「状態」とは、 がチェックされているときにもチェックされるプロパティとフィールドを指しEquals()
ます。すべての public、internal、および protected メンバーが含まれていると想定します。
私がC#オブジェクトを持っているとすれば
GetHashCode と Equals をオーバーライドします
このオブジェクトはキー値としてディクショナリに保存されます (ディクショナリはこの時点で GetHashCode 値を読み取ることに注意してください)。
Key でオブジェクトを検索し、値を変更します。(この値を変更すると、カスタム equals 関数が変更され、場合によっては gethashcode が変更されます)
私の質問は、GetHashCode は何を反映する必要があるかということです。この関数の戻り値は、オブジェクトの元の状態または変更された状態を反映する必要がありますか?
サンプルコード
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TrustMap
{
class Program
{
static Dictionary<Model.TrustedEntityReference, string> testDictionary = new Dictionary<Model.TrustedEntityReference, string>();
static void Main(string[] args)
{
Model.TrustedEntity te = new Model.TrustedEntity();
te.BackTrustLink = null;
te.ForwardTrustLink = null;
te.EntryName = "test1";
var keyValue = new Model.TrustedEntityReference()
{
HierarchyDepth = 1,
trustedEntity = te
};
testDictionary.Add(keyValue, "some data");
// Now that I have a reference to the Key outside the object
te.EntryName = "modified data";
// Question: how should TE respond to the change, considering that it's a part of a dictionary now?
// If this is a implementation error, how should I track of objects that are stored as Keys that shouldn't be modified like I did in the previous line?
}
}
}
namespace Model
{
public class TrustedEntity
{
public TrustedEntity()
{
this.BackTrustLink = new List<TrustedEntityReference>();
this.ForwardTrustLink = new List<TrustedEntityReference>();
}
public List<TrustedEntityReference> BackTrustLink { get; set; }
public string EntryName { get; set; }
public List<TrustedEntityReference> ForwardTrustLink { get; set; }
}
public class TrustedEntityReference
{
public int HierarchyDepth { get; set; }
public TrustedEntity trustedEntity {get; set; }
public override bool Equals(object obj)
{
if (obj.GetType() != trustedEntity.GetType())
return false;
TrustedEntity typedObj = (TrustedEntity)obj;
if (typedObj.BackTrustLink != null)
{
if (trustedEntity.BackTrustLink != typedObj.BackTrustLink)
return false;
}
if (typedObj.ForwardTrustLink != null)
{
if (trustedEntity.ForwardTrustLink != typedObj.ForwardTrustLink)
return false;
}
if (trustedEntity.EntryName != typedObj.EntryName)
return false;
return true;
}
/// <summary>
/// If the hash-code for two items does not match, they may never be considered equal
/// Therefore equals may never get called.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
// if two things are equal (Equals(...) == true) then they must return the same value for GetHashCode()
// if the GetHashCode() is equal, it is not necessary for them to be the same; this is a collision, and Equals will be called to see if it is a real equality or not.
// return base.GetHashCode();
return StackOverflow.System.HashHelper.GetHashCode<int, TrustedEntity>(this.HierarchyDepth, this.trustedEntity);
}
}
}
namespace StackOverflow.System
{
/// <summary>
/// Source https://stackoverflow.com/a/2575444/328397
///
/// Also it has extension method to provide a fluent interface, so you can use it like this:
///public override int GetHashCode()
///{
/// return HashHelper.GetHashCode(Manufacturer, PartN, Quantity);
///}
///or like this:
///public override int GetHashCode()
///{
/// return 0.CombineHashCode(Manufacturer)
/// .CombineHashCode(PartN)
/// .CombineHashCode(Quantity);
///}
/// </summary>
public static class HashHelper
{
public static int GetHashCode<T1, T2>(T1 arg1, T2 arg2)
{
unchecked
{
return 31 * arg1.GetHashCode() + arg2.GetHashCode();
}
}
public static int GetHashCode<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3)
{
unchecked
{
int hash = arg1.GetHashCode();
hash = 31 * hash + arg2.GetHashCode();
return 31 * hash + arg3.GetHashCode();
}
}
public static int GetHashCode<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3,
T4 arg4)
{
unchecked
{
int hash = arg1.GetHashCode();
hash = 31 * hash + arg2.GetHashCode();
hash = 31 * hash + arg3.GetHashCode();
return 31 * hash + arg4.GetHashCode();
}
}
public static int GetHashCode<T>(T[] list)
{
unchecked
{
int hash = 0;
foreach (var item in list)
{
hash = 31 * hash + item.GetHashCode();
}
return hash;
}
}
public static int GetHashCode<T>(IEnumerable<T> list)
{
unchecked
{
int hash = 0;
foreach (var item in list)
{
hash = 31 * hash + item.GetHashCode();
}
return hash;
}
}
/// <summary>
/// Gets a hashcode for a collection for that the order of items
/// does not matter.
/// So {1, 2, 3} and {3, 2, 1} will get same hash code.
/// </summary>
public static int GetHashCodeForOrderNoMatterCollection<T>(
IEnumerable<T> list)
{
unchecked
{
int hash = 0;
int count = 0;
foreach (var item in list)
{
hash += item.GetHashCode();
count++;
}
return 31 * hash + count.GetHashCode();
}
}
/// <summary>
/// Alternative way to get a hashcode is to use a fluent
/// interface like this:<br />
/// return 0.CombineHashCode(field1).CombineHashCode(field2).
/// CombineHashCode(field3);
/// </summary>
public static int CombineHashCode<T>(this int hashCode, T arg)
{
unchecked
{
return 31 * hashCode + arg.GetHashCode();
}
}
}
}
Jon Skeet からのこの回答に基づいて(以前の質問に対して)
「最終的にキーの値を変更するプロパティを変更したら、どうすればよいですか?」- 自分
.
「基本的に、あなたは詰め物をしています。辞書でそのキーを再び見つけることはできません (または、少なくともおそらく不可能です)。できる限り慎重にこれを避ける必要があります。個人的には、通常、辞書キーの有力な候補は、不変性の有力な候補でもあります。」-JS
これは、辞書からオブジェクトを削除して、再度追加する必要があるということですか? これは適切な/最善の方法ですか?