3

クラスがあるとしましょう

public class MyClass
{
    public string Type { get; set; }
    public int Id { get; set; }
}

単純に厳密に型指定された List であるコレクション クラスがあります。

public class MyClassList : List<MyClass>
{
    public MyClassList(IEnumerable<MyClass> enumerable) : base (enumerable) {}
}

コンテンツに基づいてMyClassList独自のハッシュコードを生成できるようにしたい。MyClassListのハッシュ コードはMyClass、両方のプロパティに基づく必要があります。オブジェクトの順序が異なっていても、のハッシュ コードはMyClassList同じである必要があります。

順序付けの問題を処理するために、ハッシュ コードを生成する前にリストを順序付けできると考えていましたが、リストのハッシュ コードを生成する方法がわかりません。

4

4 に答える 4

5

GetHashCode最適なパフォーマンスを得るために、呼び出されるたびにコレクション全体を反復しないようにします。の目的は、GetHashCodeすべての要素を評価するよりもパフォーマンスを向上させることです。リスト内の要素がこのように変更された場合は、ハッシュ コードを維持してみるかもしれません。

class Program
{
  static void Main(string[] args)
  {
     MyClassList l = new MyClassList() { new MyClass() {Type="Bob", Id=1}, new MyClass() {Type="Jones", Id=2}};
     MyClassList l2 = new MyClassList() { new MyClass() { Type = "Jones", Id = 2 }, new MyClass() { Type = "Bob", Id = 1 } };
     MyClassList l3 = new MyClassList() { new MyClass() { Type = "Jones", Id = 2 }};
     Console.WriteLine("{0} {1} {2}", l.GetHashCode(), l2.GetHashCode(), l3.GetHashCode());
     l3.Add(new MyClass() { Type = "Bob", Id = 1 });
     Console.WriteLine("{0}", l3.GetHashCode());
  }
}

public class MyClass
{
  public string Type { get; set; }
  public int Id { get; set; }
  public override int GetHashCode()
  {
     return (Type.GetHashCode() % 0x8000) | (int)((uint)Id.GetHashCode() & 0xFFFF0000);
  }
}

public class MyClassList : IList<MyClass>
{
  List<MyClass> internalList;
  int hashCode = 0;

  public MyClassList()
  {
     internalList = new List<MyClass>();
  }

  private void IncludeInHash(MyClass item)
  {
     hashCode ^= item.GetHashCode();
  }

  private void ExcludeFromHash(MyClass item)
  {
     IncludeInHash(item);
  }

  public override int GetHashCode()
  {
     return hashCode;
  }

  public int IndexOf(MyClass item)
  {
     return internalList.IndexOf(item);
  }

  public void Insert(int index, MyClass item)
  {
     internalList.Insert(index, item);
     // Make sure Insert is successful (doesn't throw an exception) before affecting the hash
     IncludeInHash(item);
  }

  public void RemoveAt(int index)
  {
     MyClass reduce = internalList[index];
     internalList.RemoveAt(index);
     // Make sure RemoveAt is successful before affecting the hash
     ExcludeFromHash(reduce);
  }

  public MyClass this[int index]
  {
     get
     {
        return internalList[index];
     }
     set
     {
        MyClass reduce = internalList[index];
        internalList[index] = value;
        // Make sure these happen atomically; don't allow exceptions to prevent these from being accurate.
        ExcludeFromHash(reduce);
        IncludeInHash(value);
     }
  }

  public void Add(MyClass item)
  {
     internalList.Add(item);
     IncludeInHash(item);
  }

  public void Clear()
  {
     internalList.Clear();
     hashCode = 0;
  }

  public bool Contains(MyClass item)
  {
     return internalList.Contains(item);
  }

  public void CopyTo(MyClass[] array, int arrayIndex)
  {
     internalList.CopyTo(array, arrayIndex);
  }

  public int Count
  {
     get { return internalList.Count; }
  }

  public bool IsReadOnly
  {
     get { return false; }
  }

  public bool Remove(MyClass item)
  {
     if (internalList.Remove(item))
     {
        ExcludeFromHash(item);
        return true;
     }
     else
        return false;
  }

  public IEnumerator<MyClass> GetEnumerator()
  {
     return internalList.AsReadOnly().GetEnumerator();
  }

  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  {
     return GetEnumerator();
  }
}
于 2013-10-22T15:41:18.410 に答える
1

clto によって与えられた解決策が機能します。別の方法があります: リストを全体的な順序で並べ替えます (あいまいでない限り、どのような順序でもかまいません)。その後、通常の手段を使用してハッシュ コードを計算できます。順序独立性は必要ありません。暗号化ハッシュ関数を使用することもできます。

于 2013-10-22T15:26:52.897 に答える
1

私はこの解決策を提案します (私は Equals メソッドを実装しませんでした):

public class MyClass
{
    public string Type { get; set; }
    public int Id { get; set; }

    public override int GetHashCode()
    {
        int hash = 17;
        hash = hash + 23 * this.Type.GetHashCode();
        hash = hash + 23 * this.Id.GetHashCode();
        return hash;
    }
}

public class MyClassList : List<MyClass>
{
    public MyClassList(IEnumerable<MyClass> enumerable) : base(enumerable) { }

    public override int GetHashCode()
    {
        return this.Aggregate(17, (state, current) => state * 23 + current.GetHashCode());
    }
}

ハッシュコードを生成する方法は、匿名オブジェクトのハッシュ値を計算する Microsoft の方法から着想を得ています。

于 2013-10-22T15:18:21.933 に答える
0

順序が重要でない場合は、リストではなく本質的にセットであるコレクションを使用する必要があります。

また、通常はコレクションから継承しないのが最善です。代わりにコンポジションを使用してください。

HashSetしたがって、コレクションにはセマンティクスが設定されているため、を使用できます。

両方のプロパティをアイデンティティとして使用するにMyClassは、等しいものをオーバーライドしてハッシュコードの実装を取得するか、IComparer<MyClass>それができない、またはしたくない場合は を作成します。

public class MyClass:IEquatable<MyClass>
{
    public string Type { get; set; }
    public int Id { get; set; }

    public override bool Equals(object obj)
    {
        return Equals(obj as MyClass);
    }

    public bool Equals(MyClass other)
    {
        if (other == null)
            return false;

        return Type == other.Type &&
            Id == other.Id;
    }

    public override int GetHashCode()
    {
        return Type.GetHashCode() * 79 + Id;
    }
}

次に、コレクションは次のように単純です。

HashSet<MyClass> set = new HashSet<MyClass>();

そして、さまざまなセットを比較したい場合は、次を使用してください。

HashSet<MyClass>.CreateSetComparer();
于 2013-10-22T15:30:39.020 に答える