0

以下を取得するには、より堅牢な辞書のような構造が必要でした。

  • キーを提供することによる値 (デフォルトの辞書の動作)。
  • 値を提供することによるキー (それほど些細なことではありません)。

また、キーが複数の値に関連付けられるように、この辞書のような構造の機能を拡張したいと考えました。

このディスカッションを通じて、この回答とこの他の回答は、それを実装するためのツールを提供しました。BiDictionary (Jon Skeet の回答) から「インデックスによるアクセス」を削除することにしました。これは、あいまいさが必要以上に頻繁に発生するためです (たとえば、文字列を文字列にマッピングする場合)。私が思いついた辞書のような「構造」は次のとおりです。

using System.Collections.Generic;

public interface IBiLookup<TLeft, TRight>
{
    IDictionary<TLeft, ICollection<TRight>> LeftToRight { get; }
    IDictionary<TRight, ICollection<TLeft>> RightToLeft { get; }

    bool TryGetByLeft(TLeft left, out ICollection<TRight> rights);
    bool TryGetByRight(TRight right, out ICollection<TLeft> lefts);

    void Add(TLeft left, TRight right);
}
public class BiLookup<TLeft, TRight> : IBiLookup<TLeft, TRight>
{
    public IDictionary<TLeft, ICollection<TRight>> LeftToRight
    {
        get { return this.leftToRight; }
    }
    public IDictionary<TRight, ICollection<TLeft>> RightToLeft
    {
        get { return this.rightToLeft; }
    }

    public bool TryGetByLeft(TLeft left, out ICollection<TRight> rights)
    {
        return LeftToRight.TryGetValue(left, out rights);
    }
    public bool TryGetByRight(TRight right, out ICollection<TLeft> lefts)
    {
        return RightToLeft.TryGetValue(right, out lefts);
    }

    public void Add(TLeft left, TRight right)
    {
        AddLeftToRight(left, right);
        AddRightToLeft(right, left);
    }

    private void AddLeftToRight(TLeft left, TRight right)
    {
        ICollection<TRight> rights;

        // 1) Is there an entry associated with the "left" value?
        // 2) If so, is the "right" value already associated?
        if (!TryGetByLeft(left, out rights))
        {
            // Then we have to add an entry in the leftToRight dictionary.
            rights = new List<TRight> { right };                
        }
        else
        {
            // So there are entries associated with the "left" value.
            // We must verify if the "right" value itself is not there.
            if (((List<TRight>)rights).FindIndex(element => element.Equals(right)) < 0)
            {
                // We don't have that association yet.
                rights.Add(right);
            }
            else
            {
                // The value is already in the list: do nothing.
                return;
            }
        }

        LeftToRight[left] = rights;
    }
    private void AddRightToLeft(TRight right, TLeft left)
    {
        ICollection<TLeft> lefts;

        // 1) Is there an entry associated with the "right" value?
        // 2) If so, is the "left" value already associated?
        if (!TryGetByRight(right, out lefts))
        {
            // Then we have to add an entry in the leftToRight dictionary.
            lefts = new List<TLeft> { left };
        }
        else
        {
            // So there are entries associated with the "right" value.
            // We must verify if the "right" value itself is not there.
            if (((List<TLeft>)lefts).FindIndex(element => element.Equals(left)) < 0)
            {
                // We don't have that association yet.
                lefts.Add(left);
            }
            else
            {
                // The value is already in the list: do nothing.
                return;
            }
        }

        RightToLeft[right] = lefts;
    }

    #region Fields

    private IDictionary<TLeft, ICollection<TRight>> leftToRight = new Dictionary<TLeft, ICollection<TRight>>();
    private IDictionary<TRight, ICollection<TLeft>> rightToLeft = new Dictionary<TRight, ICollection<TLeft>>();

    #endregion
}

Add(...) メソッドを 2 つのより具体的なメソッドに分割することが適切な実装であるかどうかが心配です。なぜなら、BiLookup クラスは広範囲に使用され、パフォーマンスを念頭に置いておく必要があるからです。また、このスレッドの目的は、インターフェイスとクラスの実装が可能な限り優れているかどうか、またはそれらの改善点について議論することです。

インターフェイスと「デフォルト」の実装の両方をクラス ライブラリ プロジェクトに保存した場合、その設計は再利用するのに十分ですか?

4

1 に答える 1

0

「再利用できるデザイン」かどうかは、実際に使ってみないとわかりません。いくつかの異なる方法でコードを使用してみる必要があります (すべての単体テストは 1 つとしてカウントされます)。それが理にかなっていて、読みやすいコードを作成できるかどうかを確認する必要があります。

特定の要件 (パフォーマンスまたはその他) がない場合は、コードの単体テストのカバレッジが良好であることを確認するだけで、すべてのテストに合格し、予想される使用シナリオがテストによってカバーされます。

コードに関する注意: キャスト (つまり((List<TLeft>)lefts))は不要に見えます。FindIndex呼び出し (O(number_of_items) を使用した線形検索) は、パフォーマンスに敏感なコードではうまく機能しません。

于 2013-03-05T17:29:58.267 に答える