在转换泛型类型时出错

时间:2014-10-23 08:05:36

标签: c# generics

我有一个方法ExecCommand,它以Command<CommandResult>为参数。但是当我尝试传递一个带有CommandResult派生类型的命令时,它无法构建:

class Program
{
    class CommandResult
    {
    }

    class Command<TResult> where TResult : CommandResult, new()
    {
        internal virtual TResult ParseReply(object reply)
        {
            return new TResult();
        }
        public Action<TResult> CommandCompleteCallback = null;
    }

    class CommandA : Command<CommandResult>
    {
    }

    class CommandResultForB : CommandResult
    {
    }

    class CommandB : Command<CommandResultForB>
    {
        internal override CommandResultForB ParseReply(object reply)
        {
            return new CommandResultForB();
        }
    }

    static Queue<Command<CommandResult>> commandQueue = new Queue<Command<CommandResult>>();

    static void ThreadLoop()
    {
        // This threadloop transmits the first command on the queue to external library when executeNextCommand is set (it's using Peek, so the command stays in the queue until the external library calls OnCommandCompleteResponse()
    }
    static void OnCommandCompleteRespose(object reply)
    {
        // called from external library when command is complete
        lock (commandQueue)
        {
            var command = commandQueue.Dequeue();
            if (command.CommandCompleteCallback != null)
                command.CommandCompleteCallback(command.ParseReply(reply));
        }
    }
    static void ExecCommand(Command<CommandResult> command)
    {
        lock (commandQueue)
        {
            commandQueue.Enqueue(command);
            if (commandQueue.Count == 1)
                executeNextCommand.Set();
        }
    }

    static void Main(string[] args)
    {
        ExecCommand(new CommandA());
        ExecCommand(new CommandB()); // <-- this is the offending line
    }
}

为什么我收到错误'无法从CommandB转换为命令'的任何想法?为什么不能自动将CommandResultForB强制转换为基类CommandResult

2 个答案:

答案 0 :(得分:3)

此异常的原因是默认情况下通用参数 covariant

在.net 3.5中添加了对此的支持,但您需要通过界面和out关键字对其进行定义:

interface ICommand<out TResult> where TResult : CommandResult, new()
{
   TResult ParseReply(object reply);
}


class Command<TResult> : ICommand<TResult> where TResult 
                       : CommandResult, new()
{}

然后,您可以更新ExecCommand方法以期望接口:

static void ExecCommand(ICommand<CommandResult> command){}

一旦完成,您的ExecCommand电话就可以找到:

static void Main(string[] args)
{
    ExecCommand(new CommandA());
    ExecCommand(new CommandB()); // <-- works now           
}

答案 1 :(得分:1)

如果您想使用ExecuteCommand()类型的实例调用CommandB并且仍然尽可能通用,请使用以下内容:

static void ExecCommand<TResult>(Command<TResult> command) 
    where TResult : CommandResult, new()
{
    TResult res = command.ParseReply(null);
}

注意:这是您原始问题的答案。它可能有助于理解问题的一部分,并可能帮助其他人。