2011-10-15 52 views
2

我正在构建一个简单的“公共汽车”作为概念证明。我不需要任何复杂的东西,但是想知道如何优化下面的代码。我使用Autofac作为容器来解析命令作为开放泛型,但实际上执行命令目前正在通过反射完成,因为传入命令不能转换为代码中的具体类型。查看代码 - 用// BEGIN // END标记 - 目前正在使用反射。有没有办法做到这一点,而不使用反射?如何摆脱这段代码中的反射调用?

// IoC wrapper 
static class IoC { 
    public static object Resolve(Type t) { 
     // container gubbins - not relevant to rest of code. 
    } 
} 

// Handler interface 
interface IHandles<T> { 
    void Handle(T command); 
} 

// Command interface 
interface ICommand { 
} 

// Bus interface 
interface IBus { 
    void Publish(ICommand cmd); 
} 

// Handler implementation 
class ConcreteHandlerImpl : IHandles<HelloCommand> { 
    public void Handle(HelloCommand cmd) { 
     Console.WriteLine("Hello Command executed"); 
    } 
} 

// Bus implementation 
class BusImpl : IBus { 
    public void Publish(ICommand cmd) { 
     var cmdType = cmd.GetType(); 
     var handler = IoC.Resolve(typeof(IHandles<>).MakeGenericType(cmdType)); 
     // BEGIN SLOW 
     var method = handler.GetType().GetMethod("Handle", new [] { cmdType }); 
     method.Invoke(handler, new[] { cmd }); 
     // END SLOW 
    } 
} 

回答

5

这个怎么样(仅变更部位): -

// Handler interface 
interface IHandles<T> where T : ICommand { 
    void Handle(T command); 
} 

// Bus interface 
interface IBus { 
    void Publish<T>(T cmd) where T : ICommand; 
} 

// Bus implementation 
class BusImpl : IBus { 
    public void Publish<T>(T cmd) where T : ICommand { 
     var handler = (IHandles<T>)IoC.Resolve(typeof(IHandles<T>)); 
     handler.Handle(cmd); 
    } 
} 

这里的关键是使Publish方法通用的,这意味着你得到一个类型的参考T到的类型该命令然后可以用来进行演员。类型参数约束只是确保只能传递一个ICommand,和以前一样。

顺便说一句 - 我测试过这一点,它的工作原理,这里是全码: -

public static void Main(){ 
    new BusImpl().Publish(new HelloCommand()); 
} 

// IoC wrapper 
static class IoC { 
    public static object Resolve(Type t) { 
     return new ConcreteHandlerImpl(); 
    } 
} 

// Handler interface 
interface IHandles<T> where T : ICommand { 
    void Handle(T command); 
} 

// Command interface 
interface ICommand { 
} 


// Handler implementation 
class ConcreteHandlerImpl : IHandles<HelloCommand> { 
    public void Handle(HelloCommand cmd) { 
     Console.WriteLine("Hello Command executed"); 
    } 
} 

public class HelloCommand:ICommand{} 

// Bus interface 
interface IBus { 
    void Publish<T>(T cmd) where T : ICommand; 
} 

// Bus implementation 
class BusImpl : IBus { 
    public void Publish<T>(T cmd) where T : ICommand { 
     var handler = (IHandles<T>)IoC.Resolve(typeof(IHandles<T>)); 
     handler.Handle(cmd); 
    } 
} 

- 更新 -

正如彼得Lillevold指出的那样,你也应该考虑增加类型参数到您的IOC容器的方法如下: -

// IoC wrapper 
static class IoC { 
    public static T Resolve<T>() { 
     ... 
    } 
} 

这将简化您的来电者,像这样: -

// Bus implementation 
class BusImpl : IBus { 
    public void Publish<T>(T cmd) where T : ICommand { 
     var handler = IoC.Resolve<IHandles<T>>(); 
     handler.Handle(cmd); 
    } 
} 

这是您的原始问题的一个侧面,但似乎是一个明智的IOC接口设计。

+0

现在添加通用约束并进行测试 - 感谢回复。将成功发布(希望)。 – Deleted

+0

非常好 - 非常感谢。完美的作品!我可以看到约束如何在不破坏运行时或编译器的情况下强制类型被强制。好的解决方案再次感谢。 – Deleted

+0

嗯......我在这里错过了什么吗?为什么不只是'var handler = IoC.Resolve >();'?你已经知道'T'类型了......不需要动态地生成泛型... –

0

这工作?您的IoC正在返回对象,请考虑返回T,而不必像下面那样处理类型歧义。

public void Publish(ICommand cmd) { 
    var cmdType = cmd.GetType(); 
    var handler = IoC.Resolve(typeof(IHandles<>).MakeGenericType(cmdType)) as IHandles<ICommand>; 
    if (handler != null) 
    { 
     // BEGIN SLOW 
     handler.Handle(command); 
     // END SLOW 
    } 
    //else throw some exception 
} 
+0

否 - 那将永远不会运行空检查块中的内容。 Resolve返回一个Object,如果不抛出异常就不能抛出它。那是我开始的地方。看起来你不能上传impl,所以它可以被调用,因此反射混乱。如果强制执行,实际上会得到无效的强制转换异常 - 在这种情况下,无法强制转换类型“ConcreteHandlerImpl”以键入“IHandles'1 [ICommand]”。我不确定你是否可以用协变/逆变来做到这一点。 – Deleted

+0

@Chris Smith:你的ConcreteHandlerImpl是什么样的? –

+0

很难看到发生了什么,您的IoC可能会获得一种不使用该界面的类型。 – CRice