代码之家  ›  专栏  ›  技术社区  ›  djdd87

如何动态实现代理模式?

  •  3
  • djdd87  · 技术社区  · 14 年前

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

    在玩了实体框架并阅读了 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 属性。

    public class CustomerProxy : Customer
    {
        Customer _customer;
    
        public override string Name 
        {
            get { return _customer.Name; }
            set { IsDirty = true; return _customer.Name; }
        }
    
        // Other properties
    }
    

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

    2 回复  |  直到 14 年前
        1
  •  2
  •   Kirk Woll    14 年前

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

    允许您提供拦截器:

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

    您可以通过调用方法. 您可以通过设置调用.ReturnValue. 您可以访问(并重写)参数。

        2
  •  2
  •   Jordão    14 年前

    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