Generate intercepting proxy from concrete class?
I need to create a proxy which intercepts properties in a class. I know how to create a dynamic proxy with Emit from an interface, but what if I don't have an interface? I've seen samples which use RealProxy (like this one: Is there a way to call a method when a开发者_如何转开发ny property of a class is set?) but is it possible to use type generation and emit to achieve the same thing? I don't want the "owner" of the concrete class to see any traces of MarshalByRefObject if possible (see below)...
I believe Castle is able to do this, but maybe it's using RealProxy under the covers?
User user = Create<User>();
public class User
{
public string Name { get; set; }
}
public T Create<T>()
{
//magic happens here... :)
return (T)GenerateInterceptingProxyFromT(typeof(T));
}
I just started messing with postshrp, one of the AOP tools Miguel mentioned, do functionally what you are trying to do. It uses "static weaving" to inject code at compile time so should be invisible to consumers. Obviously, you need to modify the code that you want to instrument for this to work.
The answer to This question suggests using the profiler API which may be an option for you if PostSharp, or Castle won't do what you need.
There are some options on intercepting things in .Net:
If it is an interface, you can implement a new type from that dynamically, and make a proxy, that would re-call the other inner object.
If it is an abstract class or a class that allows overrides, you can inherit from it and override the desired members dynamically, and do whatever you want.
If the type you want to intercept has no interfaces, nor overridable methods, or properties, then you must change the assembly that constains that type, before it loads. You cannot change the code of an assembly after it has been loaded. I think that PostSharp works this way.
Most of the mocking tools, used for testing purposes uses the first/second alternatives, but that makes them work only with member of the classes that are overridable, or implemented through an interface.
Aspect Oriented Programming tools use the third alternative, but it is more work to do, because you need to process the assembly before it is loaded.
Since this is a very common problem and a great reason to choose an AOP approach as Miguel suggested, I created an example for Afterthought that demonstrates implementing INotifyPropertyChanged (intercepting property sets to raise an event).
Afterthought lets you describe interceptions for properties very easily, and specifically makes property set interception simple by providing you the before and after values of the property. You would do something like this to identify the properties to intercept:
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>;
}
Which in this case calls a static method OnPropertyChanged
which looks like this:
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));
}
So if your original property looked like this:
string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
It would look like this after applying the above amendment using Afterthought:
string name;
public string Name
{
get
{
return name;
}
set
{
string oldValue = Name;
name = value;
NotificationAmender<ConcreteClass>.OnPropertyChanged<string>(
this, "Name", oldValue, value, Name);
}
}
In your case, the static method called after (or before) the setter could be named anything you want and do anything you want. This is just an example of a concrete and well known reason to intercept property setters. Given that you know the properties are non-virtual, it is not possible to create proxy subclasses to perform the interception, so I think AOP approaches like Afterthought or PostSharp are your best bet.
Also, with Afterthought you can implement the interception such that the resulting assemblies do not have any references or dependencies on Afterthought and if your interception logic does not actually add/change the API for your target types, there is no reason the "owner" of the concrete class would have a problem with the result.
精彩评论