【问题标题】:Generate intercepting proxy from concrete class?从具体类生成拦截代理?
【发布时间】:2011-08-30 07:11:50
【问题描述】:

我需要创建一个代理来拦截类中的属性。我知道如何使用 Emit 从接口创建动态代理,但是如果我没有接口怎么办?我见过使用 RealProxy 的示例(例如:Is there a way to call a method when any property of a class is set?),但是是否可以使用类型生成和发射来实现相同的目标?如果可能的话,我不希望具体类的“所有者”看到 MarshalByRefObject 的任何痕迹(见下文)......

我相信 Castle 能够做到这一点,但也许它在幕后使用了 RealProxy?

User user = Create<User>();

public class User
{
    public string Name { get; set; } 
}

public T Create<T>()
{
    //magic happens here... :)
    return (T)GenerateInterceptingProxyFromT(typeof(T));
}

【问题讨论】:

    标签: c# dynamic-proxy


    【解决方案1】:

    我刚刚开始使用 postshrp,这是 Miguel 提到的 AOP 工具之一,它可以在功能上完成您想要做的事情。它使用“静态编织”在编译时注入代码,因此对消费者来说应该是不可见的。显然,您需要修改要检测的代码才能使其正常工作。
    This question 的答案建议使用分析器 API,如果 PostSharp 或 Castle 无法满足您的需求,这可能是您的一个选择。

    【讨论】:

    • 正如您指出的那个问题所建议的那样,除非您使用某种形式的后 AOP 东西,否则探查器 API 似乎是唯一的方法,我无法在此使用不幸的是。
    【解决方案2】:

    .Net 中有一些拦截东西的选项:

    • 如果它是一个接口,您可以动态地实现一个新类型,并制作一个代理,它会重新调用另一个内部对象。

    • 如果是抽象类或者允许覆盖的类,你可以从它继承,动态覆盖想要的成员,为所欲为。

    • 如果要拦截的类型没有接口,也没有可覆盖的方法或属性,则必须在加载之前更改包含该类型的程序集。程序集加载后,您无法更改程序集的代码。我认为 PostSharp 就是这样工作的。

    大多数用于测试目的的模拟工具都使用第一种/第二种替代方法,但这使得它们只能与可重写或通过接口实现的类的成员一起使用。 p>

    Aspect Oriented Programming 工具使用第三种替代方法,但需要做更多的工作,因为您需要在加载程序集之前对其进行处理。

    【讨论】:

    • 我没有接口,类也不是抽象的,也没有虚属性。请参阅上面的用户类示例。也许这是不可能的?我想我需要调查 Castle DynamicProxy 是如何做事的 :)
    【解决方案3】:

    由于这是一个非常常见的问题,也是选择 Miguel 建议的 AOP 方法的重要理由,我 created an exampleAfterthought 演示了实现 INotifyPropertyChanged(拦截属性集以引发事件)。

    Afterthought 让您可以非常轻松地描述对属性的拦截,特别是通过为您提供属性的前后值,使属性集拦截变得简单。你会做这样的事情来识别要拦截的属性:

    public override void Amend<TProperty>(Property<TProperty> property)
    {
        // Raise property change notifications
        if (property.PropertyInfo.CanRead && property.PropertyInfo.CanWrite)
            property.AfterSet = NotificationAmender<T>.OnPropertyChanged<TProperty>;
    }
    

    在本例中调用静态方法OnPropertyChanged,如下所示:

    public static void OnPropertyChanged<P>(INotifyPropertyChangedAmendment instance, string property, P oldValue, P value, P newValue)
    {
        // Only raise property changed if the value of the property actually changed
        if ((oldValue == null ^ newValue == null) || (oldValue != null && !oldValue.Equals(newValue)))
            instance.OnPropertyChanged(new PropertyChangedEventArgs(property));
    }
    

    如果你的原始属性是这样的:

    string name;
    public string Name 
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
        }
    }
    

    使用 Afterthought 应用上述修改后,它看起来像这样:

    string name;
    public string Name 
    {
        get
        {
            return name;
        }
        set
        {
            string oldValue = Name;
            name = value;
            NotificationAmender<ConcreteClass>.OnPropertyChanged<string>(
                this, "Name", oldValue, value, Name);
        }
    }
    

    在您的情况下,在 setter 之后(或之前)调用的静态方法可以命名为您想要的任何名称并执行您想要的任何操作。这只是拦截属性设置器的具体且众所周知的原因的一个示例。鉴于您知道属性是非虚拟的,因此不可能创建代理子类来执行拦截,所以我认为像 Afterthought 或 PostSharp 这样的 AOP 方法是您最好的选择。

    此外,使用 Afterthought,您可以实现拦截,这样生成的程序集就没有对 Afterthought 的任何引用或依赖,并且如果您的拦截逻辑实际上并未为您的目标类型添加/更改 API,则没有理由“具体类的“所有者”会对结果产生问题。

    【讨论】:

      猜你喜欢
      • 2020-04-28
      • 2017-03-19
      • 2015-05-16
      • 1970-01-01
      • 2018-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-16
      相关资源
      最近更新 更多