1. 使用Struct.
1) 使用Struct,如果成员都是简单类型,默认情况下,第一个成员相同的时候,GetHashCode 值相同。如果成员值全部相同,则两个Struct相等。
2) Struct 因为要使用到反射,GetHashCode 和Equals效率比较低
2. 使用Class
1) 必须实现GetHashCode,重载Equals方法(不然每次new 一个Class作为查找Key, HashCode都不一样)。最好是实现IEquatable<ConfigCacheKeyClass>接口,以避免装箱。
2)多个属性个可以分别调用GetHashCode(),得到一个值乘以一个质数,然后相加。避免溢出加上uncheck.
测试用例代码
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
namespace AllTests
{
[TestClass]
public class UnitTest1
{
/// <summary>
/// 1. 对于一个值类型,如果其本身存在某个数据可以唯一标明此类型对象,有点儿像数据库中的Key字段,那么用它作为类型的第一个元素
/// 2. 要保证这个类型的对象通过GetHashCode能自始至终一样,就要防止第一个成员被修改,比较好的做法就是给它加上readonly标示
/// 3. 不同版本的.netframework对于值类型的GetHashCode的实现可能实现不同
/// </summary>
[TestMethod]
public void TestStructHash()
{
var t1 = new KeyStruct("AA1", "1");
var t2 = new KeyStruct("AA1", "2");
var t3 = new KeyStruct("AA1", "1");
var t4 = new KeyStruct("BB1", "1");
Debug.WriteLine(t1.GetHashCode());
Debug.WriteLine(t2.GetHashCode());
Debug.WriteLine(t3.GetHashCode());
Debug.WriteLine(t4.GetHashCode());
// 不同的.netframework可能实现不同。有的版本下面test返回True. 当前版本False.
Debug.WriteLine(t1.TestHashCode());
Hashtable ht = new Hashtable();
ht.Add(t1, "v1");
ht.Add(t2, "v2");
// exceptpion
//ht.Add(t3, "test3");
Assert.AreEqual(t1, t2);
}
[TestMethod]
public void TestClassHash1()
{
var t1 = new KeyClass("AA1", "1");
var t2 = new KeyClass("AA1", "2");
var t3 = new KeyClass("AA1", "1");
Debug.WriteLine(t1.GetHashCode());
Debug.WriteLine(t2.GetHashCode());
Debug.WriteLine(t3.GetHashCode());
#region
// 不会先调用GetHashCode函数, 再调用Equals函数判断相等。
// 如果重写了Equals函数,一定要重写GetHashCode函数来达到一致
// 如果实现了IEquatable<ConfigCacheKeyClass>,下面的Equals会调用bool Equals(ConfigCacheKeyClass other)
Debug.WriteLine(t1.Equals(t3));
#endregion
#region Hashtable的Add, Contains会调用GetHashCode函数和 bool Equals(object obj)
Hashtable ht = new Hashtable();
ht.Add(t1, "v1");
ht.Add(t2, "v2");
ht.Add(t3, "v3");
Debug.WriteLine(ht.Contains(t3));
#endregion
#region Dictionary的Add, Contains会调用GetHashCode函数和 bool Equals(ConfigCacheKeyClass other)
IDictionary<KeyClass, string> dic = new Dictionary<KeyClass, string>();
dic.Add(t1, "v1");
dic.Add(t2, "v2");
dic.Add(t3, "v3");
Debug.WriteLine(dic.Keys.Contains(t3));
#endregion
// Assert.AreEqual(t1, t2);
Assert.AreSame(t1, t3);
}
}
/// <summary>
/// new KeyStruct(), 只要p1相同, 它们的HashCode就一样了
/// </summary>
public struct KeyStruct
{
public readonly string P1;
public readonly string P2;
public KeyStruct(string p1, string p2)
{
this.P1 = (p1 == null ? null : p1.ToLowerInvariant());
this.P2 = (p2 == null ? null : p2.ToLowerInvariant());
}
public bool TestHashCode()
{
return this.GetHashCode() == this.P1.GetHashCode();
}
}
/// <summary>
/// 如果重写了Equals函数,一定要重写GetHashCode函数来达到一致
/// </summary>
public class KeyClass : IEquatable<KeyClass>
{
public readonly string P1;
public readonly string P2;
public KeyClass(string p1, string p2)
{
this.P1 = (p1 == null ? null : p1.ToLowerInvariant());
this.P2 = (p2 == null ? null : p2.ToLowerInvariant());
}
public override bool Equals(object obj)
{
// return this.ServerEnvironmentName == ((ConfigCacheKeyClass)obj).ServerEnvironmentName;
return base.Equals(obj);
}
public override int GetHashCode()
{
return this.P1.GetHashCode();
//return base.GetHashCode();
}
public bool Equals(KeyClass other)
{
return this.P1 == other.P1
&& this.P2 == other.P2;
// throw new NotImplementedException();
}
}
}