5

我需要创建一个代理来拦截类中的属性。我知道如何使用 Emit 从接口创建动态代理,但是如果我没有接口怎么办?我见过使用 RealProxy 的示例(例如:Is there a way to call a method when a class of any property 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));
}
4

3 回答 3

0

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

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

  • 如果它是一个抽象类或允许覆盖的类,您可以从它继承并动态覆盖所需的成员,并为所欲为。

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

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

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

于 2011-05-21T17:03:19.533 回答
0

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

于 2011-05-21T18:31:32.507 回答
0

由于这是一个非常常见的问题,也是 Miguel 建议的选择 AOP 方法的重要理由,我为Afterthought创建了一个示例,演示了如何实现 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,则没有理由“所有者”具体类的结果会有问题。

于 2011-05-23T03:33:54.163 回答