繁体   English   中英

如何使用反参数将通用接口转换为基本类型?

[英]How to cast generic interfaces using contravariant parameters to a base type?

我正在尝试开发通用命令处理器。 我想创建实现给定接口的命令处理程序类。 我将使用控制反转,根据收到的命令类型,动态地创建适当类的实例。 然后,我想以一种通用的方式调用该类的“ Execute”方法。

我可以使用协变类型参数来完成这项工作,但是在这种情况下,我不能将泛型类型参数用作方法参数。

似乎应该使用一个协变方法,因为它允许我根据需要声明方法参数,但是不幸的是,该类的实例无法转换为基本接口。

下面的代码示例了此问题:

using System;
using System.Diagnostics;

namespace ConsoleApplication2
{
    // Command classes

    public class CommandMessage
    {
        public DateTime IssuedAt { get; set; }
    }

    public class CreateOrderMessage : CommandMessage
    {
        public string CustomerName { get; set; }
    }

    // Covariant solution

    public interface ICommandMessageHandler1<out T> where T : CommandMessage
    {
        void Execute(CommandMessage command);
    }

    public class CreateOrderHandler1 : ICommandMessageHandler1<CreateOrderMessage>
    {
        public void Execute(CommandMessage command)
        {
            // An explicit typecast is required
            var createOrderMessage = (CreateOrderMessage) command;
            Debug.WriteLine("CustomerName: " + createOrderMessage.CustomerName);
        }
    }

    // Contravariant attempt (doesn't work)

    public interface ICommandMessageHandler2<in T> where T : CommandMessage
    {
        void Execute(T command);
    }

    public class CreateOrderHandler2 : ICommandMessageHandler2<CreateOrderMessage>
    {
        public void Execute(CreateOrderMessage command)
        {
            // Ideally, no typecast would be required
            Debug.WriteLine("CustomerName: " + command.CustomerName);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var message = new CreateOrderMessage {CustomerName = "ACME"};

            // This code works
            var handler1 = new CreateOrderHandler1();
            ICommandMessageHandler1<CreateOrderMessage> handler1b = handler1;
            var handler1c = (ICommandMessageHandler1<CommandMessage>) handler1;
            handler1c.Execute(message);

            // This code throws InvalidCastException
            var handler2 = new CreateOrderHandler2();
            ICommandMessageHandler2<CreateOrderMessage> handler2b = handler2;
            var handler2c = (ICommandMessageHandler2<CommandMessage>)handler2;  // throws InvalidCastException
            handler2c.Execute(message);
        }
    }
}

你可以施放通用接口out泛型参数只接口更具体的参数。 例如,可以将ICommandMessageHandler1<CommandMessage>强制转换为ICommandMessageHandler2<CreateOrderMessage>Execute(CommandMessage command)也将接受CreateOrderMessage ),但反之亦然。

试想一下,例如,如果允许在代码中抛出InvalidCastException ,那么如果您调用handler2c.Execute(new CommandMessage())会发生什么?

接口ICommandMessageHandler1<T>ICommandMessageHandler2<T>彼此不相关。 仅仅因为它们都有方法Execute并不能使它们兼容。 这将是鸭子式的,c#不支持。

也许我真的没有得到您想要的功能,但这对我来说没有任何类型转换的效果很好。

public class CommandMessage
{
    public DateTime IssuedAt
    {
        get;
        set;
    }
}

public class CreateOrderMessage : CommandMessage
{
    public string CustomerName
    {
        get;
        set;
    }
}

public interface ICommandMessageHandler2<in T> where T : CommandMessage
{
    void Execute(T command);
}
public class CreateOrderHandler2 : ICommandMessageHandler2<CreateOrderMessage>
{
    public void Execute(CreateOrderMessage command)
    {
        // No typecast is required
        Debug.WriteLine("CustomerName: " + command.CustomerName);
    }
}

class Program
{
    static void Main(string[] args)
    {
        var message = new CreateOrderMessage
        {
            CustomerName = "ACME"
        };

        // This code throws InvalidCastException
        var handler2 = (ICommandMessageHandler2<CreateOrderMessage>)new CreateOrderHandler2();
        handler2.Execute(message);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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