简体   繁体   中英

Equality constraint between two type parameters

I have the following issue :

interface :

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

    TOut Read<TKey>(TKey input);
}

then I have several implementations like this one :

    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;
        }
    }

but one of them already knows 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)
    {
        ...
    }
}

Based on another post I was able to write :

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();
}

but I find it very ugly.

Any better option? I hope this explanation is clear enough.

Thanks for your help.

If you don't ever plan on using MultiReader as an IReader then why bother making it implement the IReader interface? The idea is that you'd be able to store the reference as an IReader and use the object interchangeably with any other type that implements IReader . That doesn't seem to be the case with MultiReader .

In my opinion, MultiReader should not implement IReader because it seems like you don't intend to use it via the IReader interface. Be wary of violating the Liskov Substitution Principle .

Since IReader<TOut> is contract, there is no way to hide that method. Interface requires exposing method that meets specified cotnract signature. But.. you can do a little trick here with explicit interface implementation:

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();
    }
}

Then:

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

But you can still do

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

So that is why you still need cast-check in implementation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM