【问题标题】:KeyValuePair Covariance键值对协方差
【发布时间】:2013-02-01 07:54:30
【问题描述】:

在这个例子中是否有更好的方法来模拟协方差?理想情况下,我想做:

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

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

但是KeyValuePair&lt;TKey, TValue&gt; 不是协变的。

相反,我必须这样做:

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

有没有更好/更清洁的方法?

【问题讨论】:

    标签: c# c#-4.0 covariance keyvaluepair


    【解决方案1】:

    不幸的是,KeyValuePair&lt;TKey, TValue&gt; 是一个结构体;并且结构在 .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;
        }
    }
    

    【讨论】:

      【解决方案2】:

      几乎没有。 KVP 是一个结构:不是 itnerface,是 ValueType。

      有趣的SO post 差异。

      我认为强制转换的性能更高,所以我更喜欢这样的代码:

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

      然后在我真正需要的地方将KeyValuePair.Value 转换为ICollection。坦率地说,这取决于 foos 的使用方式。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-08-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多