在C#中有一个很好的强类型方法来做PropertyChanged事件吗?

时间:2009-07-14 21:16:38

标签: c# inotifypropertychanged strong-typing

更改属性名称并期望Visual Studio中的重命名功能必须处理所有必需的重命名,但INotifyPropertyChanged的PropertyChanged事件的属性名称除外。有没有更好的方法以某种方式获得强类型,所以你不需要记得手动重命名它?

7 个答案:

答案 0 :(得分:8)

编辑:nameof抵达c#6。是的!


没有nameof / infoof等;这是很多讨论,但它就是它。

有一种方法可以在.NET 3.5中使用lambda表达式(并解析表达式树),但实际上它不值得花费。现在,我只是坚持使用字符串(如果您决定不打破它,则进行单元测试)。


using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
class Program : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;
    static void Main() {
        var p = new Program();
        p.PropertyChanged += (s, a) => Console.WriteLine(a.PropertyName);
        p.Name = "abc";
    }
    protected void OnPropertyChanged<T>(Expression<Func<Program, T>> property) {
        MemberExpression me = property.Body as MemberExpression;
        if (me == null || me.Expression != property.Parameters[0]
              || me.Member.MemberType != MemberTypes.Property) {
            throw new InvalidOperationException(
                "Now tell me about the property");
        }
        var handler = PropertyChanged;
        if (handler != null) handler(this,
          new PropertyChangedEventArgs(me.Member.Name));
    }
    string name;
    public string Name {
        get{return name;}
        set {
            name = value;
            OnPropertyChanged(p=>p.Name);
        }
    }
}

答案 1 :(得分:6)

C#5似乎有一个解决方案。 CallerMemberName attribute可以与参数(One example on the net)一起使用。

class Employee : INotifyPropertyChanged
{
    private string _Name;
    public string Name
    {
        get { return _Name; }

        set
        {
            _Name = value;
            RaisePropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged([CallerMemberName] string caller = "")
    {
        var temp = PropertyChanged;

        if ( temp != null )
        {
            temp( this, new PropertyChangedEventArgs( caller ) );
        }
    }
}

答案 2 :(得分:2)

最简单的解决方案是查看堆栈跟踪并完全删除对该属性的每个显式引用。

public String Name
{
    get { return this.name; }
    set
    {
        if (value != this.name)
        {
            this.RaisePropertyChanging();
            this.name = value;
            this.RaisePropertyChanged();
        }
    }
}
private String name = null;

private void RaisePropertyChanged()
{
    String propertyName =
       new StackTrace().GetFrame(1).GetMethod().Name.SubString(4);

    PropertyChangedEventHandler handler = this.PropertyChanged;
    if (handler != null)
    {
        handler(new PropertyChangedEventArgs(propertyName));
    }
}

代码通过caling方法的堆栈跟踪派生属性名称 - 即名为set_<PropertyName>的属性setter方法。如果编译器不再遵循此命名约定,则代码会中断。

另一个解决方案是从lambda表达式派生属性名称。

public static String GetPropertyNameFromLambdaExpression<TObject, TProperty>(
    Expression<Func<TObject, TProperty>> expression)
{
    return ((MemberExpression)expression.Body).Member.Name;
}

例如

GetPropertyNameFromLambdaExpression<String, Int32>(s => s.Length)

会像预期的那样返回“Length”。代码的生产版本确实需要额外的检查并更好地集成到其余代码中。例如,可以对泛型参数使用类型推断。

<强>更新

还有第三个解决方案 - 您可以在属性getter或setter中使用MethodBase.GetCurrentMethod()来获取setter或getter方法的名称。

public String Name
{
    get { return this.name; }
    set
    {
        if (value != this.name)
        {
            String propertyName = MethodBase.GetCurentMethod().Name.SubString(4);

            this.RaisePropertyChanging(propertyName);
            this.name = value;
            this.RaisePropertyChanged(propertyName);
        }
    }
}
private String name = null;

答案 3 :(得分:1)

理论上,您可以在属性setter中使用MethodBase.GetCurrentMethod()。Name.Substring(4)。不幸的是,谷歌搜索显示it seems to have a significant performance impact。还有两件事需要考虑:

  • JIT内联会以意想不到的方式影响这一点。 (stackoverflow.com/questions/616779/can-i-check-if-the-c-compiler-inlined-a-method-call)
  • 理论上,对ActionBase.GetCurrentMethod()的IL调用可以在运行时通过ldtoken指令然后调用MethodBase.GetMethodFromHandle()来简单地替换为JIT,这将非常快。我猜用户只是没有表达对此的需求。 (msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.ldtoken.aspx)
  • 完全是我的观点,但我认为在C#中使用fieldof()和methodof()运算符会很好。我相信它会大大提高需要这种能力的项目中代码分析/重构工具的可靠性。

答案 4 :(得分:0)

不是您的问题的答案,但如果您右键单击 - >重写 - >重新命名属性,它也可以重命名匹配的字符串,包括与您的媒体资源名称相匹配的任何字符串。

是的,它可能有点危险。

答案 5 :(得分:0)

您应该查看此blog post。它使您能够执行此操作:

string propertyName = TypeHelper.GetPropertyName<User>(u => u.LastProjectCode);

PropertyInfo property1 = TypeHelper.GetProperty((SomeClass o) => o.InstanceProperty.Length);

PropertyInfo property2 = TypeHelper.GetProperty(() => SomeClass.StaticProperty.Length);

Visual Studio / Resharper / Refactor Pro中的重命名应该适合你。

答案 6 :(得分:-1)

PropertyChangedEventArgs只需要一个构造函数,它需要将属性名称作为字符串。所以基本上没有利用INotifyPropertyChanged意味着在某种程度上,无论你的架构是高还是低,你都必须使用字符串和手动重命名。