加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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;
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读