我有一种情况,我想拦截.NET中的属性调用。我一直在看Castle中的DynamicProxy,它似乎工作正常。但似乎为了使用它我必须从一个新对象开始,这意味着我不能做这样的事情:
MyType myType = new MyType();
myType.Property = "Test";
...
MyType wrappedMyType = proxyBuilder.Wrap(myType, new MyInterceptor());
wrappedMyType.Property = "Test2";
我只是错过了什么?
编辑:
哦,上帝,它当然应该包裹着MyType。大错。抱歉。 :(
答案 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'使你有效拦截了这个电话。