通用方法调用,不想发送我的类型

时间:2012-02-28 13:00:31

标签: c# generics cqrs

我正在尝试使用我的命令和一些通用方法。我没有那么多使用仿制药,所以现在我的大脑都搞砸了,希望得到一些帮助。

我想要的是最后一次通过测试。返回我的TResult而不需要在电话中提供TCommand,TResult。

invoker.Execute(command)

由于该命令实现

: CommandBase<TestResult>

我认为编译器会解决这个问题。

但是编译器只将我带到了void方法。

提前非常感谢!

编辑:完整代码位于:http://codepaste.net/7rjg2e

CommandInvoker

public interface ICommandInvoker
{
    void Execute<TCommand>(TCommand command) where TCommand : ICommand;
    TResult Execute<TCommand, TResult>(TCommand command) where TCommand : ICommand<TResult>;
}

public class CommandInvoker : ICommandInvoker
{
    ...

    public void Execute<TCommand>(TCommand command) where TCommand : ICommand
    {
        var handler = _container.GetInstance<ICommandHandler<TCommand>>();
        handler.Handle(command);

        _session.SaveChanges();
    }

    public TResult Execute<TCommand, TResult>(TCommand command) where TCommand : ICommand<TResult>
    {
        var handler = _container.GetInstance<ICommandHandler<TCommand>>();
        handler.Handle(command);

        return command.Result;
    }
}

命令

public interface ICommand
{
    bool IsValid { get; }
}

public interface ICommand<TResult> : ICommand
{
    TResult Result { get; }
}

public class CommandBase : ICommand
{
    public bool IsValid
    {
        get { return false; }
    }
}

public class CommandBase<TResult> : ICommand<TResult>
{
    public bool IsValid { get { return false; } }
    public TResult Result { get; set; }
}

CommandHandler

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

public interface ICommandHandlerWithResult<TCommand, TResult> where TCommand : ICommand<TResult>
{
    void Handle(TCommand command);
}

测试类

public class TestCommandWithResult : CommandBase<TestResult>
{
    public string Id { get; set; }
}

public class TestResult
{
    public string Message { get; set; }
}

测试有效

[Test]
public void CanExcecuteWithResult()
{
    var command = new TestCommandWithResult { Id = "billy" };

    ObjectFactory.ResetDefaults();
    var mockHandler = new Mock<ICommandHandler<TestCommandWithResult>>();
    var sessionMock = new Mock<ISession>();
    ObjectFactory.Configure(x => x.For<ICommandHandler<TestCommandWithResult>>().Use(mockHandler.Object));

    var invoker = new CommandInvoker(ObjectFactory.Container, sessionMock.Object);
    var result = invoker.Execute<TestCommandWithResult, TestResult>(command);

    mockHandler.Verify(x => x.Handle(command));
}

测试我想传递

    [Test]
    public void CanExcecuteWithResult()
    {
        var command = new TestCommandWithResult { Id = "billy" };

        ObjectFactory.ResetDefaults();
        var mockHandler = new Mock<ICommandHandler<TestCommandWithResult>>();
        var sessionMock = new Mock<ISession>();
        ObjectFactory.Configure(x => x.For<ICommandHandler<TestCommandWithResult>>().Use(mockHandler.Object));

        var invoker = new CommandInvoker(ObjectFactory.Container, sessionMock.Object);
        var result = invoker.Execute(command); // <-- this only calls void version

        mockHandler.Verify(x => x.Handle(command));
    }

2 个答案:

答案 0 :(得分:2)

这是来自语言规范中的类型推断规则。

约束where TCommand : ICommand<TResult>在确定推断类型时不起作用。

因此,类型推断算法仅使用

Execute<TComamnd, TResult>(TCommand command)

推断出TResult的类型。但参数TCommand command中没有信息来确定类型TResult

因此,过载

TResult Execute<TCommand, TResult>(TCommand command)

不是适用的函数成员,因为它无法找出TResult

从7.5.2开始

  

如果特定方法的类型推断失败,则该方法不参与重载解析。

此处,TResult的类型推断失败。

答案 1 :(得分:1)

除非将TResult作为参数传入,否则编译器无法确定TResult是什么。它能够确定TCommand的类型是什么,因为传入了它。

我没有看到你需要TCommand的任何理由,你有一个足够好的接口,你需要通用的唯一地方是返回,作为建议尝试改变ICommandInvoker的签名,它将给你你想要什么:

public interface ICommandInvoker
{
    void Execute(ICommand command);
    TResult Execute<TResult>(ICommand<TResult> command);
}

编辑:

实际上我确实知道你为什么要TCommand,因为你在容器上使用泛型类型,但我的答案仍然存在。您可以将Type传递给容器而不是通用吗?

public TResult Execute<TResult>(ICommand<TResult> command)
{
    Type commandHandlerType = typeof(ICommandHandler<>).MakeGenericType(command.GetType());

    var handler = _container.GetInstance(commandHandlerType);
    handler.Handle(command);

    return command.Result;
}