我的目标是让方法init()
和complete(result)
在所选类的每个方法之前和之后运行。
类似于ASP.NET中的ActionFilterAttribute
具有OnActionExecuting
和OnActionExecuted
方法的方式,这些方法在应用它们的任何方法之前和之后运行。
我希望将多个接口应用于相同的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
那样简单易懂的方法来实现我的目标吗?
答案 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实现锁定。