从具体类生成拦截代理?

时间:2011-05-21 16:48:22

标签: c# dynamic-proxy

我需要创建一个拦截类中属性的代理。我知道如何使用接口创建一个带有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));
}

3 个答案:

答案 0 :(得分:0)

在.Net中拦截事物有一些选择:

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

  • 如果它是一个抽象类或允许覆盖的类,您可以从它继承并动态覆盖所需的成员,并做任何你想做的事。

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

用于测试目的的大多数模拟工具使用第一个/第二个替代方案,但这使得它们只能用于可覆盖的类的成员,或通过接口实现。

面向方面编程工具使用第三种方法,但还有更多工作要做,因为您需要在装配之前处理装配。

答案 1 :(得分:0)

我刚刚开始搞乱postshrp,Miguel提到的AOP工具之一,在功能上做了你想要做的事情。它使用“静态编织”在编译时注入代码,因此应该对消费者不可见。显然,您需要修改要为此工作的代码。
This question的答案建议使用分析器API,如果PostSharp或Castle不能满足您的需求,可能是您的选择。

答案 2 :(得分:0)

由于这是一个非常常见的问题,并且是Miguel建议的选择AOP方法的一个很好的理由,created an exampleAfterthought演示了实现INotifyPropertyChanged(拦截属性集以引发事件)。< / p>

Afterthought让您可以非常轻松地描述属性的拦截,特别是通过为您提供属性的before和after值,使属性集拦截变得简单。您可以执行以下操作来识别要拦截的属性:

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

在使用事后考虑应用上述修正后,它看起来像这样:

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

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

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