【问题标题】:How do I dynamically implement the Proxy Pattern?如何动态实现代理模式?
【发布时间】:2010-09-13 16:43:24
【问题描述】:

我正在重新审视我去年写的课程跟踪(肮脏的逻辑)。目前我有一个处理所有状态跟踪的 uber 基类,但是我需要跟踪的每个属性的值都需要遵循标准的 get { return _x; } set { _isDirty = true; _x = value; } 工作方式。

在玩过 Entity Framework 并阅读了 Proxy Pattern 之后,我希望有一种更好的方法来实现我的 IsDIrty 逻辑,同时能够利用自动实现的属性?

老实说,我不知道我在说什么。有没有办法可以执行以下操作:

public class Customer : ITrackable
{
    [TrackState(true)] // My own attribute
    public virtual string Name { get;set;}

    [TrackState(true)]
    public virtual  int Age { get;set;}

    // From ITrackable
    public bool IsDirty { get; protected set; }

}

然后实现一个动态代理,该代理将使用反射(或其他神奇的解决方案)首先调用另一个方法,然后再使用TrackState 属性设置属性的值。

显然,我可以通过创建物理代理类并使用 IoC 轻松做到这一点:

public class CustomerProxy : Customer
{
    Customer _customer;

    public override string Name 
    {
        get { return _customer.Name; }
        set { IsDirty = true; return _customer.Name; }
    }

    // Other properties
}

但我不想对每个对象都这样做,否则我现有的解决方案没有任何好处。希望有人能满足我的好奇心,或者至少告诉我EF是如何实现的。

【问题讨论】:

标签: design-patterns c#-4.0 proxy-pattern


【解决方案1】:

Castle 的 DynamicProxy 正是这样做的:http://www.castleproject.org/dynamicproxy/index.html

允许您提供拦截器:

public void Intercept(IInvocation invocation)
{
    // Call your other method first...  then proceed
    invocation.Proceed();
}

您可以通过 invocation.Method 访问 MethodInfo 对象。您可以通过设置 invocation.ReturnValue 来覆盖返回值。您可以访问(和覆盖)参数。

【讨论】:

    【解决方案2】:

    PostSharp 可以提供帮助。

    或者,如果您愿意,可以为此编写自己的 IL 重写器。 Mono.Cecil 是一个很棒的图书馆,它会让它变得轻而易举。这是速溶:

    class Program {
    
      static ModuleDefinition _module;
    
      static void Main(string[] args) {
        // the argument is the assembly path
        _module = ModuleDefinition.ReadModule(args[0]);
        var trackables = _module.Types.
          Where(type => type.Interfaces.Any(tr => tr.Name == "ITrackable"));
        var properties = trackables.SelectMany(type => type.Properties);
        var trackableProperties = properties.
          Where(property => property.CustomAttributes.
            Any(ca => ca.Constructor.DeclaringType.Name == "TrackStateAttribute"));
        trackableProperties.
          Where(property => property.SetMethod != null).
          ToList().
          ForEach(property => CallIsDirty(property.SetMethod));
        _module.Write(args[0]);
      }
    
      private static void CallIsDirty(MethodDefinition setter) {
        Console.WriteLine(setter.Name);
    
        var isDirty = setter.DeclaringType.Methods.
          Single(method => method.Name == "set_IsDirty");
        var reference = new MethodReference(isDirty.Name,
          _module.Import(typeof(void))) {
            DeclaringType = setter.DeclaringType,  
            HasThis = true,
            CallingConvention = MethodCallingConvention.Default
          };
        reference.Parameters.Add(new ParameterDefinition(
          _module.Import(typeof(bool))));
        var IL = setter.Body.GetILProcessor();
        var param0 = IL.Create(OpCodes.Ldarg_0);
        var param1 = IL.Create(OpCodes.Ldc_I4_1);
        var call = IL.Create(OpCodes.Call, reference);
        IL.InsertBefore(setter.Body.Instructions[0], call);
        IL.InsertBefore(setter.Body.Instructions[0], param1);
        IL.InsertBefore(setter.Body.Instructions[0], param0);
      }
    }
    

    它使用这些助手:

    public class TrackStateAttribute : Attribute { }
    
    public interface ITrackable { bool IsDirty { get; } }
    

    示例代码:

    public class Customer : ITrackable {
      [TrackState] public string Name { get; set; }
      [TrackState] public int Age { get; set; }
      public bool IsDirty { get; protected set; }
    }
    

    假设IsDirty 属性也会有一个setter。

    【讨论】:

    • +1 谢谢!我的项目中已经有Castle.Core,所以我选择了 DynamicProxy 而不是这个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 2015-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多