5

この問題に対する提案として誰かがいるのだろうかと思っています。

私はカスタム IEqualityComparer で intersect and except (Linq) を使用して、セットの違いをクエリし、ISyncableUsers の 2 つのシーケンスの交差を設定しています。

public interface ISyncableUser
{
    string Guid { get; }
    string UserPrincipalName { get; }
}

2 つの ISyncableUsers が等しいかどうかの背後にあるロジックは条件付きです。条件は、Guid と UserPrincipalName の 2 つのプロパティのいずれかに値があるかどうかに集中します。このロジックを説明する最良の方法は、コードを使用することです。以下は、私の顧客である IEqualityComparer の Equals メソッドの実装です。

public bool Equals(ISyncableUser userA, ISyncableUser userB)
{
    if (userA == null && userB == null)
    {
        return true;
    }

    if (userA == null)
    {
        return false;
    }

    if (userB == null)
    {
        return false;
    }

    if ((!string.IsNullOrWhiteSpace(userA.Guid) && !string.IsNullOrWhiteSpace(userB.Guid)) &&
        userA.Guid == userB.Guid)
    {
        return true;
    }

    if (UsersHaveUpn(userA, userB))
    {
        if (userB.UserPrincipalName.Equals(userA.UserPrincipalName, StringComparison.InvariantCultureIgnoreCase))
        {
            return true;
        }
    }
    return false;
}

private bool UsersHaveUpn(ISyncableUser userA, ISyncableUser userB)
{
    return !string.IsNullOrWhiteSpace(userA.UserPrincipalName)
            && !string.IsNullOrWhiteSpace(userB.UserPrincipalName);
}

私が抱えている問題は、上記の条件付き等価性が尊重されるように GetHashCode を実装することです。私が intersect 呼び出しと except 呼び出しを期待どおりに機能させることができた唯一の方法は、GetHashCode() から常に同じ値を単純に返し、Equals への呼び出しを強制することです。

 public int GetHashCode(ISyncableUser obj)
 {
     return 0;
 }

これは機能しますが、予想どおり、パフォーマンスが大幅に低下します。(私はこれを無条件の等価性でテストしました。50000 個のオブジェクトを含む 2 つのセットで、適切なハッシュコード実装により、約 40 ミリ秒でインターセプトと例外を実行できます。常に 0 を返すハッシュコード実装には、約 144000 ミリ秒かかります (はい、2.4 分です!) )

では、上記のシナリオで GetHashCode() を実装するにはどうすればよいでしょうか?

どんな考えでも大歓迎です!

4

3 に答える 3

2

私がこれを正しく読んでいれば、あなたの平等関係は推移的ではありません. 次の 3 つISyncableUserの s を想像してください。

A { Guid: "1", UserPrincipalName: "2" }
B { Guid: "2", UserPrincipalName: "2" }
C { Guid: "2", UserPrincipalName: "1" }
  • A == B彼らは同じだからUserPrincipalName
  • B == C彼らは同じだからGuid
  • A != Cどちらも共有しないからです。

スペックから、

Equalsメソッドは、再帰的、対称的、および推移的です。つまり、オブジェクトをそれ自体と比較するために使用された場合はtrueを返します。2 つのオブジェクトに対してtrueであり、xおよびに対してtrueyの場合。および2 つのオブジェクトについてtrueであり、 および についてtrueであり、およびについてもtrueである場合。yxxzxyyz

等式関係が一貫していない場合、それをバックアップするハッシュ コードを実装する方法はありません。

別の観点から言えば、基本的に次の 3 つの関数を探しています。

  • GGUID を int にマッピングする (GUID はわかっているが UPN が空白の場合)
  • UUPN から int へのマッピング (UPN はわかっているが GUID が空白の場合)
  • P(guid、upn) ペアを int にマッピング (両方を知っている場合)

そのようなG(g) == U(u) == P(g, u)すべてのgu. gこれは、u完全に無視した場合にのみ可能です。

于 2012-11-02T14:50:17.277 に答える
0

1 つの方法は、ユーザー名と GUIDS のハッシュコードの辞書を維持することです。

  • このディクショナリを最初にすべてのユーザーに対して 1 回生成することができます。これはおそらく最もクリーンなソリューションです。

  • 各ユーザーのコンストラクターでエントリを追加または更新できます。

  • または、GetHashCode 関数内でその辞書を維持することもできます。これは、GetHashCode 関数が行うべき作業が多く、副作用がないわけではないことを意味します。これを複数のスレッドまたは parallel-linq で動作させるには、より慎重な作業が必要になります。したがって、このアプローチをお勧めするかどうかはわかりません。

それにもかかわらず、ここに私の試みがあります:

private Dictionary<string, int> _guidHash = 
     new Dictionary<string, int>();

private Dictionary<string, int> _nameHash = 
     new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

public int GetHashCode(ISyncableUser obj)
{
    int hash = 0;

    if (obj==null) return hash;

    if (!String.IsNullOrWhiteSpace(obj.Guid) 
        && _guidHash.TryGetValue(obj.Guid, out hash))
        return hash;

    if (!String.IsNullOrWhiteSpace(obj.UserPrincipalName) 
        && _nameHash.TryGetValue(obj.UserPrincipalName, out hash))
        return hash;

    hash = RuntimeHelpers.GetHashCode(obj); 
    // or use some other method to generate an unique hashcode here

    if (!String.IsNullOrWhiteSpace(obj.Guid)) 
         _guidHash.Add(obj.Guid, hash);

    if (!String.IsNullOrWhiteSpace(obj.UserPrincipalName)) 
         _nameHash.Add(obj.UserPrincipalName, hash);

    return hash;
}

ISyncableUser オブジェクトが適切に再生されず、Rawling の回答のようにケースが表示されない場合、これは失敗することに注意してください。同じ GUID を持つユーザーは同じ名前を持つか、まったく名前がなく、同じ principalName を持つユーザーは同じ GUID を持つか、まったく GUID を持たないと想定しています。(指定された Equals 実装には同じ制限があると思います)

于 2012-11-02T15:13:02.963 に答える