Wrapping existing objects to intercept method/property calls in .NET
I have a situation where I would like to intercept calls to properties in .NET. I have been looking at DynamicProxy in Castle and it seems to work fine. But it seems in order to use it I have to start with a new object, meaning I can't do something like this:
MyType myType = new MyType();
myType.Property = "Test";
...
MyType wrappedMyType = proxyBuilder.Wrap(myType, new MyInterceptor());
wrappedMyType.Property = "Test2";
Am I just missing somethin开发者_C百科g?
EDIT:
Oh god, it should of course be wrappedMyType. Big mistake. Sorry. :(
It doesn't work like that, it doesn't change the original object in any way.
Think of it like this. Let's consider moving to China, and working for a Chinese company, that will only pay your salary to a Chinese bank account in a Chinese bank.
So, you need to get a Chinese bank account. Problem is, that the bank you want to use, doesn't speak english, so you have a problem.
What you could do, if this was available, would be to call up a proxy service, a translator service, that on your behalf, calls the bank. Anything you say to this proxy person, will be translated to chinese, and said to the bank official. Anything he/she responds with in chinese will be translated back to english, and spoken to you.
In effect, you can now do something along the communication line when talking to your bank.
However, it does not make your bank officials speak english.
The proxy object, from your example, does not modify the underlying object. Whenever you call methods on your proxy objects, they will in turn call methods on the underlying object, possible doing processing along the way.
But if you sidestep the proxy object, nothing has changed.
You can't do this, and for good reasons. This is not specific to Castle Windsor. The issue is that you have no guarantee that the methods are marked as virtual
and therefore you have an inconsistency where there is some state coming from the wrapped object and some state coming from the proxy object.
Think of the following very simple example:
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++; }
}
Let's say that we want to create a proxy for AbstractPerson
to intercept 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
}
}
Notice that we can't override Age
because it's not marked as virtual
. This is where the yucky state inconsistencies will come from:
Person knuth = new Person(71);
PersonProxy proxy = new PersonProxy(knuth);
Console.WriteLine(knuth.Age);
knuth.Birthday();
Console.WriteLine(knuth.Age);
Console.WriteLine(proxy.Age);
This will print
71
72
0
to the console. What happened? Because Age
is not marked as virtual, our proxy object can't override the base behavior and call wrappedPerson.Age
. This example even shows that adding Age = wrappedPerson.Age
to the constructor for PersonProxy
will not help. Our proxy isn't really a proxy. This is why you can not wrap existing objects.
PostSharp may be usable for you, depending on exactly what you want to do with the "interception", and if you can modify the original code.
For this to be a viable option you must be able to add attributes to the original properties that you want to intercept. (I'm guessing this is not an option in your case, but can't tell for sure.) If you are able to do this, you can create an attribute (derived from OnMethodBoundaryAspect) which can both set the 'ReturnValue' and 'FlowBehavior' such that you've effectively intercepted the call.
You could probably do this using the System.Reflection.Emit.TypeBuilder but it wouldn't be easy and probably wouldn't work on all types. For instance you couldn't do it on sealed types because in order to maintain the ability to use your type normally you would have to inherit from it in the type you build and you would have to either override or shadow every property on the base class. On top of that you would have to emit the IL into the body of the properties set methods when you override it to raise an event or something.
All of this is possible but not easy and not perfect. You would probably be better off with another solution. I do enjoy this kind of stuff though so maybe if I get some time I'll update this answer with a code sample (Sorry I'm supose to be "working").
Update: The more I think I about this it's not going to happen. Maybe if the objects you're trying to wrap always implement an interface and you only want to intercept those members. I thought about posting a sample for that but I think it would pollute this question. Best case scenario you would end up in the situation described in Jason's answer.
精彩评论