2016-01-30 49 views
1

我的目標是讓方法init()complete(result)在選定類的每個方法之前和之後運行。 類似於ASP.NET中的ActionFilterAttribute如何將OnActionExecutingOnActionExecuted方法應用到應用它們的任何方法之前和之後。在每次調用接口之前和之後運行特定方法

有多個接口我想應用相同的init()complete(result)方法,並且我希望避免代碼重複並儘可能讓代碼易於理解。

從我可以告訴,似乎沒有一個優雅的解決方案。到目前爲止,我有3個選項:

選項1:

public interface MyImportantMethods 
    { 
     object f(); 
     object g(); 
    } 

    public class MyClass : MyImportantMethods 
    { 
     public object f() 
     { 
      // Do things 
     } 

     public object g() 
     { 
      // Do things 
     } 
    } 

    public class WrappedMyClass : MyImportantMethods 
    { 
     private MyImportantMethods ActualContent; 

     public MyModifiedClass(MyImportantMethods actualContent) 
     { 
      this.ActualContent = actualContent; 
     } 

     public object f() 
     { 
      init(); 
      object result = this.ActualContent.f(); 
      complete(result); 
      return result; 
     } 

     public object g() 
     { 
      init(); 
      object result = this.ActualContent.g(); 
      complete(result); 
      return result; 
     } 
    } 
  • 優點:與實現的類是從 這就要求init()complete()了一個完全獨立的,所以它更易讀和易於理解。
  • 缺點:不熟悉代碼的人很容易使用 錯誤的代碼。我希望 適用於每個接口需要不同的類。

選項2:

public interface MyImportantMethods 
    { 
     object f(); 
     object g(); 
    } 


    class Wrapper: IDisposable 
    { 
     public object result; 
     public Wrapper() 
     { 
      init(); 
     } 

     void Dispose() 
     { 
      complete(result); 
     } 
    } 

    public class MyModifiedClass { 
     private void f() 
     { 
      using (var wrapper = new Wrapper(() => completeF())) 
      { 
       // Do Things 
       wrapper.result = result; 
      } 
     } 

     private void g() 
     { 
      using (var wrapper = new Wrapper(() => completeG())) 
      { 
       // Do Things 
       wrapper.result = result; 
      } 
     } 
    } 
  • 優點:不能使用了錯誤的類。有可能通過使用一個 IDisposable的類所有的接口,如果我使用一個反射招

  • 缺點:代碼會顯得更加混亂,很難理解爲一個 新的讀者,特別是如果包裝的聲明發生多 行或包裝需要結果中的多個參數(在我的情況下,兩個 都是正確的)。也依靠調用者來設置結果。

方案3:

​​
  • 優點:不能使用了錯誤的類。具有 實現的類與調用init() 和complete()的類完全分離,因此它更易於理解並且更易於理解。

    缺點:讀者不太容易理解。

難道真的沒有辦法像ActionFilterAttribute那樣簡單易懂嗎?

+0

恕我直言,選項3號是最好的,我不認爲這很難理解。當你提到ActionFilterAttribute時,爲什麼不創建一個屬性並在你的類中使用它? – Skaparate

+0

我也喜歡Option3:很多程序員認可的Template Method設計模式的一個例子 – alexm

+0

@Skaparate你需要一些使用屬性的東西 - 你不能指望只是添加幾個屬性就會奇蹟般地實現方法/接口攔截依賴注入控制器。如果你對OP很可能試圖做什麼感興趣,你可以看看它是如何完成Unity的(https://msdn.microsoft.com/en-us/library/dn178466(v=pandp.30).aspx)或者一般來說https://www.bing.com/search?q=c%23%20dependency%20injection%20interception –

回答

0

或許帶有工廠模式的選項1就足夠了:將構造函數設置爲私有的,因此不能以錯誤的方式使用。在同一個類中創建一個靜態函數來構造主題對象,然後在其周圍創建一個包裝對象,並返回一個普通共享接口的類型。然而,如果你有很多類來包裝,運行時代碼生成(或設計時間.tt代碼生成)可以幫助你。調用WrapperGenerator.Create(newObject)其中T是接口,newObject是實現該接口的私有構造對象。 Create函數反映在接口上,然後基於該接口即時創建一個新類,該接口將圍繞作爲基礎對象的函數進行額外的函數調用。任何新創建的包裝類都將被緩存爲相同的接口。

class WrapperGenerator 
{ 
    static Dictionary<Type,ConstructorInfo> cachedWrappers = new ...(); 
    public static T Create<T>(object wrappedObject) 
    { 
     ConstructorInfo wrapperConstructor = null; 
     var found = cachedWrappers.TryGet(typeof(T), out wrapperConstructor); 
     if (found) 
     { 
      return (T)wrapperConstructor.Invoke(wrappedObject); 
     } 

     //cachedWrapper not found 
     wrapperConstructor = GenerateNewConstructor(typeof(T)); 
     cachedWrappers.Add(typeof(T), wrapperConstructor); 

     return (T)wrapperConstructor.Invoke(wrappedObject); 
    } 

    static long Counter = 0; 
    static ConstructorInfo GenerateNewConstructor(Type t) 
    { 
     var methodList = t.GetMethods(...); 
     StringBuilder sb = new StringBuilder(); 
     sb.Append("namespace WrapperClasses {\r\n"); 
     var ClassName = "Wrapper" + Counter++; 
     var InterfaceName = t.FullName; 
     sb.AppendFormat("public class {0} {{\r\n", ClassName); 
     sb.AppendFormat("{0} wrappedObject = null;\r\n", ClassName); 
     sb.AppendFormat("public Wrapper{0}({1} wrappedObject) {"\r\n, ClassName, InterfaceName); 
     sb.Append(" this.wrappedObject = wrappedObject;\r\n"); 
     sb.Append("}\r\n"); 
     foreach (var m in methodList) 
     { 
      sb.AppendFormat("public {0} {1}({2}) {{", m.ReturnType.., m.Name, ParameterDeclarationsSegment(m)); 
      sb.AppendFormat(" init();\r\n"); 
      sb.AppendFormat(" var result = wrappedObject.{0}({1});\r\n", m.Name, ParameterPassthroughSegment(m));  
      sb.AppendFormat(" complete(result);\r\n"); 
      sb.Append("};\r\n");  
     } 
     //ETC.. closing class and namespace 
     var LoadedAssembly = Compiler.Compile(sb,...); 
     var getWrapperType = LoadedAssembly.SearchType(ClassName); 
     return getWrapperType.GetConstructors()[0]; 
    } 
} 

注意:您應該實現對cachedWrappers的鎖定。

相關問題