Why is this:Interface<T> not of type Interface<T> using an IS keyword comparison?

I am trying to figure out why using the is keyword seems to differ when I cast. Here's a test case showing what I am looking at:

[TestFixture]
public class TestMy {

    public interface IBaseMessage { }

    public interface IMessageProcessor<T> where T : IBaseMessage {
        void Process(T msg);
    }

    public class RRMessage : IBaseMessage {
    }

    public class BaseMessageProcessor {
        public bool CanProcess<T>(T msg) where T : IBaseMessage {
            return this is IMessageProcessor<T>;
        }
    }

    public class RRMessageProcessor : BaseMessageProcessor, IMessageProcessor<RRMessage> {
        public void Process(RRMessage msg) {
            throw new NotImplementedException();
        }
    }

    [Test]
    public void Test1() {
        var msgProcessor = new RRMessageProcessor();
        var msg = new RRMessage(); 
        Console.WriteLine(msgProcessor.CanProcess(msg));
    }

    [Test]
    public void Test2() {
        var msgProcessor = new RRMessageProcessor();
        var msg = new RRMessage() as IBaseMessage; 
        Console.WriteLine(msgProcessor.CanProcess(msg));
    }

Why does Test1() return True and Test2() return False. How can I implement CanProcess() so that it returns True in both cases?

Jon Skeet
people
quotationmark

The type inference in Test1 means it's calling

msgProcessor.CanProcess<RRMessage>()

... whereas in Test2 it's calling

msgProcessor.CanProcess<IBaseMessage>()

Now RRMessageProcessor doesn't implement IMessageProcessor<IBaseMessage> - you can't call Process(justAnyMessage) - it only knows how to process RRMessage values. So it's really entirely reasonable, basically.

To make both tests return true, you don't really want a generic method, because you only care about the execution-time type of msg. There are a few options here:

  • Have a private generic method, but call it with an argument of type dynamic, in which case the type inference will be performed using the execution-time type of the message:

    public bool CanProcess(IBaseMessage message)
    {
        dynamic d = message;
        // Perform type inference at execution time
        return CanProcess(d);
    }
    
    private bool CanProcess<T>(T message)
    {
        return this is IMessageProcessor<T>;
    }
    
  • Check the interfaces using reflection, e.g. calling typeof(IMessageProcessor<>).MakeGenericType(msg.GetType()).IsAssignableFrom(GetType())

Note that you still need to be slightly careful - suppose you had some SpecialRRMessage deriving from RRMessage... then an RRMessageProcessor could still reasonably process it, but it doesn't implement IMessageProcessor<SpecialRRMessage>.

You may want to change your IMessageProcessor<T> interface so that T is declared to be contravariant:

public interface IMessageProcessor<in T> where T : IBaseMessage

At that point, your RRMessageProcessor would implement IMessageProcessor<RRMessage> due to contravariance.

people

See more on this question at Stackoverflow