两个类型参数之间的等式约束

时间:2016-05-19 14:32:12

标签: c# generics

我有以下问题:

界面:

public interface IReader<TOut>
{
    IEnumerable<TOut> GetData(int from, int size);

    TOut Read<TKey>(TKey input);
}

然后我有几个这样的实现:

    public class ConcreteReader<TOut> : IReader<TOut>
    {
        private IReader<TOut> parentReader;

        public IEnumerable<TOut> GetData(int from, int size)
        {
            // do stuff, don't need TKey for this
        }

        public TOut Read<TKey>(TKey Input)
        {
            this.parentReader.Read(input);
            ... // Do my job
            return result;
        }
    }

但其中一人已经知道TKey:

public class MultiReader<TKey, TOut> : IReader<TOut>
{
    public IEnumerable<TOut> GetData(int from, int size)
    {
        // do stuff. need TKey here
    }

    // this method does the job but it can't be the implementation of IReader<TOut>.Read<TKey>
    public TOut Read(TKey input)
    {
        ...
    }

    // this is the implementation of IReader<TOut>.Read<TKey>
    // I would like to enforce TKey == TKey1 but I can't write 
    // where TKey1 : TKey because the constraint would have to be on IReader interface
    public TOut Read<TKey1>(TKey1 input)
    {
        ...
    }
}

基于另一篇文章,我能写出:

public TOut Read<TKey1>(TKey1 input)
{
    if (input is TKey)
    {
        object objectId = (object)input;
        TKey keyId = (TKey)objectId;
        return this.Read(keyId);
    }

    throw new InvalidOperationException();
}

但我发现它非常难看。

还有更好的选择吗? 我希望这个解释很清楚。

感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

如果您不打算将MultiReader用作IReader,那么为什么还要打扰它实现IReader界面呢?我们的想法是,您可以将引用存储为IReader,并将该对象与实现IReader的任何其他类型交替使用。 MultiReader似乎不是这种情况。

在我看来,MultiReader不应该实现IReader,因为您似乎不打算通过IReader界面使用它。警惕违反Liskov Substitution Principle

答案 1 :(得分:1)

由于IReader<TOut>是契约,因此无法隐藏该方法。接口需要符合指定cotnract签名的公开方法。但是......你可以通过显式接口实现来做一些小技巧:

public class MultiReader<TKey, TOut> : IReader<TOut>
{
    public TOut Read(TKey input)
    {
        return ((IReader<TOut>)this).Read<TKey>(input);
    }

    TOut IReader<TOut>.Read<TKey1>(TKey1 input)
    {
         if (input is TKey)
        {
            object objectId = (object)input;
            TKey keyId = (TKey)objectId;
            return this.Read(keyId);
        }

        throw new InvalidOperationException();
    }
}

然后:

var mr = new MultiReader<string, string>();
var test = mr.Read("someKey"); //OK
var test2 = mr.Read<int>(1); //compile-time error

但你仍然可以

var mr = new MultiReader<string, string>();
var test = mr.Read("someKey"); //OK
var test2 = ((IReader<string>)mr).Read<int>(1); //that is ok,
                                                //we use contract - we cannot prevent this

这就是为什么你仍然需要在实现中进行强制检查。