Reputation: 4032
I'm having difficulty trying to sum up my question in form of a google query (maybe my foo is off):
I'm attempting to give an interface or class some functionality by not editing the type definition itself, but rather by dressing up the object with an interface. The simple example I'm attempting is to attach a Attribute, called [Notify] which has a property change event defined to, ideally, give the interface/class use of the event. I've been unsuccessful with this so I'm opening up to any device possible (if any) to achieve the equivalent:
[Notify]
public interface IPerson
{
string Name { get; set; }
}
[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
public class Notify: Attribute
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Using an IPerson:
IPerson person = PersonFactory.create();
person.PropertyChanged += SomeDelegateHere // the center peice
Of course if you copied this code, person won't have PropertyChanged. With or without attributes, is there a way to accomplish given the IPerson more functionality without changing the definition (i.e not adding an interface or base class). I get that attributes are changing too, but I'm comfortable with the meta data approach.
Thanks in advance for any light that can be shed.
Upvotes: 0
Views: 899
Reputation: 236328
What you are looking is hooking on such events as method invocations and attaching some actions to that events. This is not default feature of C#, but it can be done via such AOP framework as PostSharp. PostSharp adds this functionality by changing code during build time (it does not use Reflection).
Here is what you need to implement with PostSharp:
[Serializable]
[IntroduceInterface(typeof(INotifyPropertyChanged), OverrideAction = InterfaceOverrideAction.Ignore)]
[MulticastAttributeUsage(MulticastTargets.Class, Inheritance = MulticastInheritance.Strict )]
public class NotifyAttribute : InstanceLevelAspect, INotifyPropertyChanged
{
[ImportMember("OnPropertyChanged", IsRequired = false)]
public Action<string> OnPropertyChangedMethod;
[IntroduceMember(Visibility = Visibility.Family, IsVirtual = true, OverrideAction = MemberOverrideAction.Ignore)]
public void OnPropertyChanged( string propertyName )
{
if (PropertyChanged != null )
PropertyChanged(Instance, new PropertyChangedEventArgs(propertyName));
}
[IntroduceMember(OverrideAction = MemberOverrideAction.Ignore)]
public event PropertyChangedEventHandler PropertyChanged;
[OnLocationSetValueAdvice]
[MulticastPointcut(Targets = MulticastTargets.Property, Attributes = MulticastAttributes.Instance)]
public void OnPropertySet( LocationInterceptionArgs args )
{
if (args.Value == args.GetCurrentValue()) return;
args.ProceedSetValue();
this.OnPropertyChangedMethod.Invoke( args.Location.Name );
}
}
Implementation details you can find here.
Usage is very simple:
[Notify]
public class Person
{
public string Name { get; set; }
}
Keep in mind, that actual code will be injected into assembly during compilation. Attributes cannot add any functionality. They are just meta data. In this case meta data used by PostSharp to generate code and inject it into assembly.
Upvotes: 1
Reputation: 18965
Attributes generally don't do anything. Their purpose is more to provide descriptors or meta data for the types/fields/properties/etc that they decorate.
A general alternative might be to use extension methods, but I don't think that'll work in your case either because you're trying to add an event to the type.
The only viable alternative I can think of would be to use something like PostSharp or other AOP framework to do some codeweaving post-compile.
Upvotes: 1