包装现有对象以拦截.NET中的方法/属性调用

时间:2009-12-18 17:34:48

标签: .net castle-dynamicproxy

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

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

...

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

我只是错过了什么?

编辑:

哦,上帝,它当然应该包裹着MyType。大错。抱歉。 :(

4 个答案:

答案 0 :(得分:3)

它不起作用,它不会以任何方式改变原始对象。

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

所以,你需要一个中国银行账户。问题是,您想要使用的银行不会说英语,所以您遇到了问题。

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

实际上,您现在可以在与银行交谈时沿着通信线执行某些操作

但是,它并不能让你的银行官员说英语。

您的示例中的代理对象不会修改基础对象。无论何时在代理对象上调用方法,它们都会在底层对象上调用方法,可能会在此过程中进行处理。

但如果你回避代理对象,一切都没有改变。

答案 1 :(得分:1)

你不能这样做,而且有充分的理由。这不是Castle Windsor特有的。问题是您无法保证这些方法被标记为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添加到PersonProxy的构造函数中也无济于事。我们的代理不是真正的代理。这就是您无法包装现有对象的原因。

答案 2 :(得分:0)

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

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

更新:我认为我对此越多,就不会发生这种情况。也许如果你想要包装的对象总是实现一个接口,你只想拦截那些成员。我想为此发布一个样本,但我认为它会污染这个问题。最好的情况是你最终会遇到Jason的答案所描述的情况。

答案 3 :(得分:0)

PostSharp 可能可供您使用,具体取决于您对“拦截”的确切要求,以及是否可以修改原始代码。

要使其成为可行选项,您必须能够将属性添加到要拦截的原始属性。 (我猜这不是你的选择,但不能确定。)如果你能够做到这一点,你可以创建一个属性(派生自OnMethodBoundaryAspect),它可以设置'ReturnValue'和'FlowBehavior'使你有效拦截了这个电话。