2

我有一种情况,我想拦截对 .NET 中属性的调用。我一直在看 Castle 中的 DynamicProxy,它似乎工作正常。但似乎为了使用它,我必须从一个新对象开始,这意味着我不能做这样的事情:

MyType myType = new MyType();
myType.Property = "Test";

...

MyType wrappedMyType = proxyBuilder.Wrap(myType, new MyInterceptor());
wrappedMyType.Property = "Test2";

我只是错过了什么吗?

编辑:

天哪,当然应该是wrappedMyType。大错。对不起。:(

4

4 回答 4

3

它不是那样工作的,它不会以任何方式改变原始对象。

像这样想。让我们考虑搬到中国,为一家中国公司工作,这只会将你的工资支付到中国银行的中国银行账户。

因此,您需要获得一个中国银行帐户。问题是,您要使用的银行不会说英语,所以您遇到了问题。

如果可以的话,你可以做的是调用代理服务,翻译服务,代表你打电话给银行。你对这个代理人说的任何话,都会被翻译成中文,然后告诉银行官员。他/她用中文回复的任何内容都将被翻译回英文,并与您交谈。

实际上,您现在可以在与银行交谈时沿着通信线路做一些事情。

但是,它不会让您的银行官员说英语。

您的示例中的代理对象不会修改基础对象。每当您调用代理对象上的方法时,它们将依次调用底层对象上的方法,并可能在此过程中进行处理。

但是,如果您回避代理对象,则什么都没有改变。

于 2009-12-18T17:45:28.203 回答
1

你不能这样做,而且有充分的理由。这并非特定于温莎城堡。问题是您无法保证这些方法被标记为virtual,因此您会遇到不一致的情况,即有些状态来自包装对象,而有些状态来自代理对象。

想想下面这个非常简单的例子:

abstract class AbstractPerson {
    public int Age { get; protected set; }
    public abstract void Birthday();
}

class Person : AbstractPerson {
    public Person(int age) { Age = age; }
    public override Birthday() { Age++; }
}

假设我们要为AbstractPerson拦截创建一个代理Birthday

class PersonProxy : AbstractPerson {
    readonly AbstractPerson wrappedPerson;

    public PersonProxy(AbstractPerson person) { 
        wrappedPerson = person;
    }
    public override void Birthday() {
        DoInterceptors();
        wrappedPerson.Birthday();
    }
    public void DoInterceptors() { 
        // do interceptors 
    }
}

请注意,我们无法覆盖Age,因为它没有标记为virtual. 这就是令人讨厌的状态不一致的来源:

Person knuth = new Person(71);
PersonProxy proxy = new PersonProxy(knuth);
Console.WriteLine(knuth.Age);
knuth.Birthday();
Console.WriteLine(knuth.Age);
Console.WriteLine(proxy.Age);

这将打印

71
72
0

到控制台。发生了什么?因为Age没有被标记为虚拟,所以我们的代理对象不能覆盖基本行为并调用wrappedPerson.Age. 此示例甚至表明添加Age = wrappedPerson.Age到构造函数 forPersonProxy将无济于事。我们的代理并不是真正的代理。这就是您不能包装现有对象的原因。

于 2009-12-18T18:02:01.573 回答
0

PostSharp 可能对您有用,具体取决于您想要对“拦截”做什么,以及您是否可以修改原始代码。

要使这是一个可行的选项,您必须能够将属性添加到要拦截的原始属性。(我猜这在您的情况下不是一个选项,但不能确定。)如果您能够做到这一点,您可以创建一个属性(从 OnMethodBoundaryAspect 派生),它可以设置“ReturnValue”和'FlowBehavior' 这样您就可以有效地拦截呼叫。

于 2009-12-18T18:48:36.817 回答
0

您可能可以使用 System.Reflection.Emit.TypeBuilder 执行此操作,但这并不容易,并且可能不适用于所有类型。例如,您不能在密封类型上执行此操作,因为为了保持正常使用您的类型的能力,您必须在您构建的类型中从它继承,并且您必须覆盖或隐藏基类上的每个属性. 最重要的是,当您覆盖它以引发事件或其他内容时,您必须将 IL 发送到属性集方法的主体中。

这一切都是可能的,但并不容易,也不完美。使用另一种解决方案可能会更好。我确实喜欢这种东西,所以也许如果我有时间我会用代码示例更新这个答案(对不起,我应该是“工作”)。

更新:我越是认为我不会发生这种情况。也许如果您尝试包装的对象总是实现一个接口并且您只想拦截这些成员。我考虑过为此发布一个样本,但我认为它会污染这个问题。最好的情况是您最终会遇到 Jason 的回答中描述的情况。

于 2009-12-18T17:53:58.467 回答