c# – 如何为公共接口类型编写自己的装饰器实用程序(动态代理如
发布时间:2020-12-15 21:21:02 所属栏目:百科 来源:网络整理
导读:我注意到有时整个框架的工作都存在于具有非常简单的基本情况(不是批评)的事物中.例如,您可以使用几行代码和哈希表创建服务定位器,也可以使用整个框架. 也就是说,我很好奇是否有一种同样简单的自己做装饰的方法. (如果我不恰当地使用这个模式的名称,请更正我,
我注意到有时整个框架的工作都存在于具有非常简单的基本情况(不是批评)的事物中.例如,您可以使用几行代码和哈希表创建服务定位器,也可以使用整个框架.
也就是说,我很好奇是否有一种同样简单的自己做装饰的方法. (如果我不恰当地使用这个模式的名称,请更正我,我对这个术语不感兴趣). 动机:我很好奇城堡动态代理的工作方式…… 你怎么能编写MyContainer.Get(之前的Action,之后的Action)? public interface IFoo { void F(); } public class Foo : IFoo { public void F() { Console.WriteLine("foo"); } } IFoo foo = MyContainer.Get<IFoo>( () => { Console.WriteLine("before foo"); },() => { Console.WriteLine("after foo"); }); foo.F(); 输出将是: before foo foo after foo 解决方法
根据我的理解,至少有两种方法可以生成运行时代码 – 如果你习惯使用IL,你可以使用
Reflection.Emit,否则你可以使用
CSharpCodeProvider,它可以让你在运行时从字符串或一系列描述DOM样式代码的对象.
这实际上是我第一次使用CSharpCodeProvider,但这是我在运行时使用它为接口创建代理类的尝试.这不是一个完整的解决方案,但有以下条件,它应该是一个不错的开始: >它不包括将Lambda转换为字符串.据我所知,this can be done. 这是代码: public static T Get<T>(Action beforeMethodCall,Action afterMethodCall) { Type interfaceType = typeof(T); // I assume MyContainer is wrapping an actual DI container,so // resolve the implementation type for T from it: T implementingObject = _myUnderlyingContainer.Resolve<T>(); Type implementingType = implementingObject.GetType(); // Get string representations of the passed-in Actions: this one is // over to you :) string beforeMethodCode = GetExpressionText(beforeMethodCall); string afterMethodCode = GetExpressionText(afterMethodCall); // Loop over all the interface's methods and create source code which // contains a method with the same signature which calls the 'before' // method,calls the proxied object's method,then calls the 'after' // method: string methodImplementations = string.Join( Environment.NewLine,interfaceType.GetMethods().Select(mi => { const string methodTemplate = @" public {0} {1}({2}) {{ {3} this._wrappedObject.{1}({4}); {5} }}"; // Get the arguments for the method signature,like // 'Type1' 'Name1','Type','Name2',etc. string methodSignatureArguments = string.Join( ",",mi.GetParameters() .Select(pi => pi.ParameterType.FullName + " " + pi.Name)); // Get the arguments for the proxied method call,like 'Name1',// 'Name2',etc. string methodCallArguments = string.Join( ",mi.GetParameters().Select(pi => pi.Name)); // Get the method return type: string returnType = (mi.ReturnType == typeof(void)) ? "void" : mi.ReturnType.FullName; // Create the method source code: return string.Format( CultureInfo.InvariantCulture,methodTemplate,returnType,// <- {0} mi.Name,// <- {1} methodSignatureArguments,// <- {2} beforeMethodCode,// <- {3} methodCallArguments,// <- {4} afterMethodCode); // <- {5} })); // Our proxy type name: string proxyTypeName = string.Concat(implementingType.Name,"Proxy"); const string proxySourceTemplate = @" namespace Proxies {{ public class {0} : {1} {{ private readonly {1} _wrappedObject; public {0}({1} wrappedObject) {{ this._wrappedObject = wrappedObject; }} {2} }} }}"; // Get the proxy class source code: string proxySource = string.Format( CultureInfo.InvariantCulture,proxySourceTemplate,proxyTypeName,// <- {0} interfaceType.FullName,// <- {1} methodImplementations); // <- {2} // Create the proxy in an in-memory assembly: CompilerParameters codeParameters = new CompilerParameters { MainClass = null,GenerateExecutable = false,GenerateInMemory = true,OutputAssembly = null }; // Add the assembly that the interface lives in so the compiler can // use it: codeParameters.ReferencedAssemblies.Add(interfaceType.Assembly.Location); // Compile the proxy source code: CompilerResults results = new CSharpCodeProvider() .CompileAssemblyFromSource(codeParameters,proxySource); // Create an instance of the proxy from the assembly we just created: T proxy = (T)Activator.CreateInstance( results.CompiledAssembly.GetTypes().First(),implementingObject); // Hand it back: return proxy; } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |