在每次调用接口实现之前和之后运行特定方法

时间:2016-01-30 02:10:31

标签: c#

我的目标是让方法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:

    public abstract class MyImportantMethodsBase
    {
        public object f()
        {
            init();
            object result = this.fImplementation();
            complete(fResult);
            return result;
        }

        public object g()
        {
            init();
            object result = this.gImplementation();
            complete(result);
            return result;
        }

        private abstract object fImplementation();

        private abstract object gImplementation();
    }

    public class MyModifiedClass : MyImportantMethodsBase {
        private object void fImplementation()
        {
            // Do Things
        }

        private object gImplementation()
        {
            // Do Things
        }
    }
  • 优点:不能使用错误的类。与班级 实现完全独立于调用init()的实现 和complete(),因此它更易于阅读和理解。

    缺点:读者不易理解。

真的没有像ActionFilterAttribute那样简单易懂的方法来实现我的目标吗?

1 个答案:

答案 0 :(得分: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实现锁定。