模拟制作库接口变体

时间:2016-06-14 15:38:41

标签: c# polymorphism covariance contravariance

有几个地方我的代码使用方差显着受益,以更好地利用多态性。我真正想做的事情,我知道是不可能的,是这样的:

abstract class A
{
    // In my perfect world, this would automatically define a compile-time type:
    // a super-type of IReadOnlyDictionary<Q, X>,
    // that only includes the members that obey the appropriate variance.
    // In the real world, this just doesn't compile.
    public abstract IReadOnlyDictionary<in Q, out X> Dict { get; }
}

class B : A
{
    // Q:R, and X:Y
    public Dictionary<R, Y> SubDict { get; private set; }
    public override IReadOnlyDictionary<in Q, out X> Dict { get { return SubDict; } }
}

class C : A
{
    // Q:S, and X:Z
    public Dictionary<S, Z> SubDict { get; private set; }
    public override IReadOnlyDictionary<in Q, out X> Dict { get { return SubDict; } }
}

foreach (A a in alist)
{
    Q q = new Q("test key");
    X x = a.Dict[key];
    Console.WriteLine(x.Information);
}

我怎样才能实现这样的目标?以下是我提出的一些解决方案,但没有一个是理想的。

  1. 使用我想要的单独基本接口创建一个完全独立的接口树,并使用Dictionary的子类或包装类实现它。在我的代码中使用它们而不是库接口。不理想,因为我的接口不包含这些接口的库版本,因此在将它们传递给库方法之前需要进行转换或转换(这对于像IList这样的事情更为重要)。
  2. 在继承树的适当位置包含库接口作为接口的基础。不理想,因为这会产生大量的歧义,这意味着我需要在我的代码中进行大量的转换。在这个注释中,有没有办法创建一个继承多个具有相同名称的成员的接口,并以某种方式将它们标记为同一个东西,以便实现者必须为它们提供单个实现?
  3. 以某种方式告诉我的代码库接口实现了我的代码。据我所知,这是不可能的。
  4. 策略1和2的代码示例:

    public interface IReadOnlyDictionaryBase
    {
        int Count { get; }
    }
    
    public interface IReadOnlyDictionaryKeyOut<out TKey> : IReadOnlyDictionaryBase
    {
        IEnumerable<TKey> Keys { get; }
    }
    
    public interface IReadOnlyDictionaryValueOut<out TValue> : IReadOnlyDictionaryBase
    {
        IEnumerable<TValue> Values { get; }
    }
    
    public interface IReadOnlyDictionaryKeyInValueOut<in TKey, out TValue> : IReadOnlyDictionaryValueOut<TValue>
    {
        TValue this[TKey key] { get; }
        bool ContainsKey(TKey key);
        TValue GetValueTry(TKey key, out bool success);
    }
    
    // A disadvantage of Strategy 1 is that this is not related to the System.Collections.Generic.IReadOnlyDictionary.
    // Strategy 2 attempts to reconcile this by adding IReadOnlyDictionary as a base interface here, but that results in ambiguity whenever I try to call one of the members.
    public interface IExtendedReadOnlyDictionary<TKey, TValue> : IReadOnlyDictionaryKeyInValueOut<TKey, TValue>, IReadOnlyDictionaryKeyOut<TKey>, IEnumerable<KeyValuePair<TKey, TValue>>
    {
        bool TryGetValue(TKey key, out TValue value);
    }
    
    public class ExtendedDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IExtendedReadOnlyDictionary<TKey, TValue>
    {
    

1 个答案:

答案 0 :(得分:0)

听起来你想要使用IReadOnlyDictionary<TKey, TValue>之类的不变界面和“声明它”作为变体。为了协变,接口必须只能输出泛型类型(如IEnumerable<T>)。所以你不能简单地采用一个不变的接口,只需通过这种方式声明它就可以变换它。

您的选项#1是可行的,但需要注意。你可以删除不能在新界面中变体的方法,只展示符合你的方差需求的方法,这会让你有类似的东西: / p>

public interface IVariantReadOnlyDictionary<in TKey, out TValue>
{
    bool ContainsKey(TKey key);
    //bool TryGetValue(TKey key, out TValue value);  // cannot be contravariant in TValue

    TValue this[TKey key] { get; }

    //IEnumerable<TKey> Keys { get; }  // cannot be covariant in TKey
    IEnumerable<TValue> Values { get; }
    int Count { get; }  

    //cannot extend IEnumerable<KeyValuePair<TKey, TValue>>
}

请注意,IReadOnlyDictionary<TKey, TValue>的两种方法无法满足您的差异需求,并且您无法以变体方式扩展IEnumerable<KeyValuePair<TKey, TValue>>。您可以转换为客户端代码中的一种变体方法,但它不是编译时安全的。

请注意,您不能:

  • 扩展“基础”界面,因为它不是变体
  • 将基本接口视为您的接口(即您无法安全地将IReadOnlyDictionary<TKey, TValue>转换为IVariantReadOnlyDictionary<TKey, TValue>。您基本上必须在实现此接口的新类型中“包装”现有字典。