2016-01-30 48 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的锁定。

相关问题