На прошлой неделе я написал перехватчик для Set, который можно легко расширить для Get, он использует RealProxy, что означает, что ваш базовый класс должен наследовать MarshalByRefObject.
Еще одна причудливая опция - иметь абстрактный класс и использовать Reflection Emit для создания конкретного класса, который объединяет все свойства.
Также вы можете посмотреть на генераторы кода, чтобы обойти это или PostSharp ...
Производительность для этого решения не звездная, но она должна быть достаточно быстрой для большинства привязок пользовательского интерфейса. Это можно улучшить, создав методы LCG для вызова прокси.
public interface IInterceptorNotifiable {
void OnPropertyChanged(string propertyName);
}
/// <summary>
/// A simple RealProxy based property interceptor
/// Will call OnPropertyChanged whenever and property on the child object is changed
/// </summary>
public class Interceptor<T> where T : MarshalByRefObject, IInterceptorNotifiable, new() {
class InterceptorProxy : RealProxy {
T proxy;
T target;
EventHandler<PropertyChangedEventArgs> OnPropertyChanged;
public InterceptorProxy(T target)
: base(typeof(T)) {
this.target = target;
}
public override object GetTransparentProxy() {
proxy = (T)base.GetTransparentProxy();
return proxy;
}
public override IMessage Invoke(IMessage msg) {
IMethodCallMessage call = msg as IMethodCallMessage;
if (call != null) {
var result = InvokeMethod(call);
if (call.MethodName.StartsWith("set_")) {
string propName = call.MethodName.Substring(4);
target.OnPropertyChanged(propName);
}
return result;
} else {
throw new NotSupportedException();
}
}
IMethodReturnMessage InvokeMethod(IMethodCallMessage callMsg) {
return RemotingServices.ExecuteMessage(target, callMsg);
}
}
public static T Create() {
var interceptor = new InterceptorProxy(new T());
return (T)interceptor.GetTransparentProxy();
}
private Interceptor() {
}
}
Использование:
class Foo : MarshalByRefObject, IInterceptorNotifiable {
public int PublicProp { get; set; }
public string lastPropertyChanged;
public void OnPropertyChanged(string propertyName) {
lastPropertyChanged = propertyName;
}
}
[Test]
public void TestPropertyInterception() {
var foo = Interceptor<Foo>.Create();
foo.PublicProp = 100;
Assert.AreEqual("PublicProp", foo.lastPropertyChanged);
}
}