简体   繁体   中英

C# casting with generic interfaces

I am trying to understand why casting does not work in this situation.

Assume the following interfaces:

public interface IDomainEvent { }

public interface IHandler<T> where T: IDomainEvent
{
    void Handle(T args);
}

And these simple implementations:

public class SomeEvent : IDomainEvent
{
    public string Value { get; set; }
}

public class SomeEventHandler : IHandler<SomeEvent>
{
    public void Handle(SomeEvent args) { }
}

I do not understand why I cannot do this cast:

var handler = new SomeEventHandler() as IHandler<IDomainEvent>;

I am using Autofac to resolve a list of handlers based on events, which it does fine, but it creates them of course fully concrete, which at runtime I need to handle them via interfaces. I can use reflection easy enough to call the handle method, but it seems like this should work :/

You can't conceptually treat an IHandler<SomeEvent> as if it's an IHandler<IDomainEvent> . An IHandler<IDomainEvent> needs to be able to accept an instance of AThirdEvent (that implements IDomainEvent ) as a parameter to Handle , but your SomeEventHandler type can't accept AThirdEvent instances, only SomeEvent instances.

The compiler is correctly informing you that, from a conceptual perspective, this type conversion is not valid.

Conceptually your interface is actually contravariant . If you had an IHandler<IDomainEvent> you could implicitly convert it to an IHandler<SomeEvent> (if you adjusted the definition of the interface to inform the compiler that it is in fact contravariant) because if your Handle method can accept any type of IDomainEvent then clearly it can accept every single SomeEvent .

This requires your interface to be covariant .

public interface IHandler<out T> : where T : IDomainEvent

However, your interface itself wouldn't support this, as it's effectively contravariant. Allowing this to work would not be type safe, and there is no direct workaround. Covariant interfaces are only allowed to return T , not accept T as a parameter to a method.

For details, see variance in generic interfaces .

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