12

この例で共分散を模倣するより良い方法はありますか? 理想的には、私はやりたいです:

private IDictionary<string, ICollection<string>> foos;

public IEnumerable<KeyValuePair<string, IEnumerable<string>> Foos
{
    get
    {
        return foos;
    }
}

しかしKeyValuePair<TKey, TValue>、共変ではありません。

代わりに、私はしなければなりません:

public IEnumerable<KeyValuePair<string, IEnumerable<string>>> Foos
{
    get
    {
        return foos.Select(x => 
            new KeyValuePair<string, IEnumerable<string>>(x.Key, x.Value));
    }
}

より良い/よりクリーンな方法はありますか?

4

2 に答える 2

8

残念ながら、KeyValuePair<TKey, TValue>構造体です。また、構造体は .NET で差異を示しません。

もちろん、これは、独自の共変Pairインターフェイスと、 のシーケンスKeyValuePairとカスタムPairインターフェイスの間で変換するいくつかの単純なヘルパーを作成することで解決できます。これにより、次のことが可能になります。

var dict = new Dictionary<string, ICollection<string>>();

var view = dict.GetCovariantView(); // IEnumerable< IPair<string, ICollection<string> > >
  
// Notice that you can _widen_ both the key and the value types:
var dictView = view.CastPairs<object, IEnumerable<string>>(); // IEnumerable< IPair< object, IEnumerable<String> > >

// The `CastPairs` call is actually unnecessary provided you don't use `var` for the left-hand-side assignment.
// ...this is due to the implicit (and identity-preserving) variant interface conversion in C#, e.g.:
IEnumerable< IPair< Object, IEnumerable<String> > > dictView2 = view;

Console.WriteLine( Object.ReferenceEquals( view, dictView2 ) ); // --> True

これを実現するコードの例を次に示します。

// `out TKey` is for demonstration purposes. In production-quality code you probably should be using invariant key types.
public interface IPair<out TKey, out TValue>
    where TKey : notnull
{
    TKey   Key   { get; }
    TValue Value { get; }
}

public class Pair<TKey, TValue> : IPair<TKey, TValue>
    where TKey : notnull
{
    public TKey   Key   { get; }
    public TValue Value { get; }

    public Pair(TKey key, TValue value)
    {
        this.Key   = key;
        this.Value = value;
    }

    public Pair(KeyValuePair<TKey, TValue> pair)
        : this(pair.Key, pair.Value)
    {}
}

public static class PairSequenceExtensions
{
    public static IEnumerable<IPair<TKey, TValue>> GetCovariantView<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source)
        where TKey : notnull
    {
        if (source is null) throw new ArgumentNullException(nameof(source));

        return source.Select(kvp => new Pair<TKey, TValue>(kvp));
    }

    public static IEnumerable<IPair<TKey, TValue>> CastPairs<TKey, TValue>(this IEnumerable<IPair<TKey, TValue>> source)
        where TKey : notnull
    {
        if (source is null) throw new ArgumentNullException(nameof(source));

        return source;
    }
}
于 2013-02-16T14:52:04.287 に答える
0

しそうにない。KVP は構造体です: Itnerface ではなく、ValueType です。

分散に関する興味深いSO 投稿。

キャストの方がパフォーマンスが高いと思うので、次のようにコーディングすることをお勧めします。

private IDictionary<string, IEnumerable<string>> foos;

public IEnumerable<KeyValuePair<string, IEnumerable<string>> Foos
{
    get
    {
        return foos;
    }
}

そして、本当に必要な場所にキャストKeyValuePair.Valueします。ICollection率直に言って、それは foos の使用方法に依存します。

于 2013-02-16T13:39:45.023 に答える