gpt4 book ai didi

c# - 接口(interface)协变逆变: why is this not compiling?

转载 作者:太空狗 更新时间:2023-10-30 00:29:57 25 4
gpt4 key购买 nike

我想实现一个 CommandBus那可以Dispatch一些 CommandsCommandHandlers .

  • A Command是一个简单的 DTO,描述应该发生什么。例如:“计数器加 5”
  • A CommandHandler能够处理精确类型的 Command .
  • CommandBus需要 Command并执行 CommandHandler能够处理它。

我写的代码无法编译。

编译器提示 cannot convert from 'IncrementHandler' to 'Handler<Command>' .我不明白为什么,因为IncrementHandler工具 Handler<Increment>Increment工具 Command

我都试过了 inout通用接口(interface)上的修饰符,它不能解决问题。

有没有办法只用接口(interface)实现这个?

[TestClass]
public class CommandBusTest
{
[TestMethod]
public void DispatchesProperly()
{
var handler = new IncrementHandler(counter: 0);
var bus = new CommandBus(handler); // <--Doesn't compile: cannot convert from 'IncrementHandler' to 'Handler<Command>'
bus.Dispatch(new Increment(5));
Assert.AreEqual(5, handler.Counter);
}
}

public class CommandBus
{
private readonly Dictionary<Type, Handler<Command>> handlers;

public CommandBus(params Handler<Command>[] handlers)
{
this.handlers = handlers.ToDictionary(
h => h.HandledCommand,
h => h);
}

public void Dispatch(Command commande) { /*...*/ }
}

public interface Command { }

public interface Handler<TCommand> where TCommand : Command
{
Type HandledCommand { get; }
void Handle(TCommand command);
}

public class Increment : Command
{
public Increment(int value) { Value = value; }

public int Value { get; }
}

public class IncrementHandler : Handler<Increment>
{
// Handler<Increment>
public Type HandledCommand => typeof(Increment);
public void Handle(Increment command)
{
Counter += command.Value;
}
// Handler<Increment>

public int Counter { get; private set; }

public IncrementHandler(int counter)
{
Counter = counter;
}
}

最佳答案

I don't understand why, because IncrementHandler implements Handler<Increment> and Increment implements Command

让我们消除你的误解,然后剩下的就清楚了。

假设您想做的是合法的。出了什么问题?

IncrementHandler ih = whatever;
Handler<Command> h = ih; // This is illegal. Suppose it is legal.

现在我们创建一个类

public class Decrement : Command { ... }

现在我们将它传递给 h:

Decrement d = new Decrement();
h.Handle(d);

这是合法的,因为Handler<Command>.Handle需要 Command , 和一个 DecrementCommand .

那到底发生了什么?您刚刚将递减命令传递给了 ih , 通过 h ,但是 ih是一个 IncrementHandler只知道如何处理增量。

既然那是荒谬的,那么这里的东西肯定是非法的;你希望哪条线是非法的? C# 团队决定转换应该是非法的。

更具体地说:

您的程序在试图绕过类型系统的安全检查进行最终运行时使用反射,然后您提示类型系统在您编写不安全的内容时阻止您。您为什么完全使用泛型?

泛型(部分)是为了确保类型安全,然后您正在执行基于反射的调度。这没有任何意义;不要采取措施来提高类型安全性,然后做出英勇的努力来解决它们

显然您希望解决类型安全问题,所以根本不要使用泛型。只需制作一个 ICommand界面和一个Handler接受命令的类,然后有一些机制来计算如何调度命令。

不过我不明白的是为什么会有两种东西。如果要执行命令,为什么不直接将执行逻辑放在命令对象上呢?

除了这种基于类型的笨拙字典查找之外,您还可以在此处使用其他设计模式。例如:

  • 命令处理程序可以有一个接受命令并返回 bool 值的方法,无论处理程序是否可以处理此命令。现在你有了一个命令处理程序列表,一个命令进来了,你只需沿着列表运行并询问“你是我的处理程序吗?”直到你找到一个。如果 O(n) 查找速度太慢,则构建 MRU 缓存或内存结果或类似的东西,摊销行为将会改善。

  • 调度逻辑可以放入命令处理程序本身。给命令处理程序一个命令;它要么执行它,要么递归调用其父命令处理程序。因此,您可以构建一个命令处理程序图,根据需要相互推迟工作。 (这基本上就是 QueryService 在 COM 中的工作方式。)

关于c# - 接口(interface)协变逆变: why is this not compiling?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44163860/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com