通过C#属性为接口/类添加功能?

时间:2012-03-28 20:02:26

标签: c# attributes custom-attributes

我很难以谷歌查询的形式总结我的问题(也许我的foo已关闭):

我试图通过不编辑类型定义本身来为接口或类提供一些功能,而是通过使用接口修饰对象。我正在尝试的一个简单示例是附加一个名为[Notify]的属性,该属性定义了一个属性更改事件,理想情况下,为事件的接口/类使用。我没有成功,所以我开放任何可能的设备(如果有的话)来实现相同的目标:

[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));
            }
        }

    }

使用IPerson:

IPerson person = PersonFactory.create();
person.PropertyChanged += SomeDelegateHere // the center peice

当然,如果你复制了这段代码,那么人就不会有PropertyChanged。有或没有属性,有没有办法实现IPerson更多的功能而不改变定义(即不添加接口或基类)。我觉得属性也在变化,但我对元数据方法感到满意。

提前感谢任何可以脱光的灯光。

2 个答案:

答案 0 :(得分:1)

属性通常不会任何事情。他们的目的更多是为他们装饰的类型/字段/属性/等提供描述符或元数据

一般的替代方法可能是使用扩展方法,但我认为这不会对您的情况有效,因为您正在尝试向该类型添加事件。

我能想到的唯一可行的替代方案是使用PostSharp或其他AOP框架之类的东西来进行编译后编译。

答案 1 :(得分:1)

您正在寻找的是挂钩方法调用等事件并将某些操作附加到该事件。这不是C#的默认功能,但可以通过PostSharp这样的AOP框架完成。 PostSharp通过在构建期间更改代码来添加此功能(它不使用Reflection)。

以下是您需要使用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 );            
    }
}

您可以找到here的实施细节。

用法非常简单:

[Notify]
public class Person
{
    public string Name { get; set; }
}

请记住,实际代码将在编译期间注入到程序集中。属性无法添加任何功能。它们只是元数据。在这种情况下,PostSharp使用元数据生成代码并将其注入汇编。