强制转换泛型类型时出错

本文关键字:出错 泛型类型 转换 | 更新日期: 2023-09-27 18:27:42

我有一个方法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转换为Command"吗?为什么不能自动将CommandResultForB强制转换为其基类CommandResult

强制转换泛型类型时出错

此异常的原因是默认情况下,泛型参数是而不是协变:

在.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           
}

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

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

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