2011-10-15 24 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