.net INotifyPropertyChanged 属性名称 - 硬编码与反射?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/141370/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 10:07:09  来源:igfitidea点击:

INotifyPropertyChanged property name - hardcode vs reflection?

.netwpfreflection

提问by Kerry Jenkins

What is the best way to specify a property name when using INotifyPropertyChanged?

使用 INotifyPropertyChanged 时指定属性名称的最佳方法是什么?

Most examples hardcode the property name as an argument on the PropertyChanged Event. I was thinking about using MethodBase.GetCurrentMethod.Name.Substring(4) but am a little uneasy about the reflection overhead.

大多数示例将属性名称硬编码为 PropertyChanged 事件的参数。我正在考虑使用 MethodBase.GetCurrentMethod.Name.Substring(4) 但对反射开销有点不安。

回答by Romain Verdier

Don't forget one thing : PropertyChangedevent is mainly consumed by components that will use reflectionto get the value of the named property.

不要忘记一件事:PropertyChanged事件主要由组件使用,这些组件将使用反射来获取命名属性的值。

The most obvious example is databinding.

最明显的例子是数据绑定。

When you fire PropertyChangedevent, passing the name of the property as a parameter, you should know that the subscriber of this event is likely to use reflectionby calling, for instance, GetProperty(at least the first time if it uses a cache of PropertyInfo), then GetValue. This last call is a dynamic invocation (MethodInfo.Invoke) of the property getter method, which costs more than the GetPropertywhich only queries meta data. (Note that data binding relies on the whole TypeDescriptorthing -- but the default implementation uses reflection.)

当您触发PropertyChanged事件时,将属性名称作为参数传递,您应该知道此事件的订阅者很可能通过调用使用反射,例如,GetProperty(如果它使用 的缓存PropertyInfo,则至少是第一次),然后GetValue. 最后一次调用是属性 getter 方法的动态调用 (MethodInfo.Invoke),其成本高于GetProperty仅查询元数据的方法。(请注意,数据绑定依赖于整个TypeDescriptor事物——但默认实现使用反射。)

So, of course using hard code property names when firing PropertyChanged is more efficient than using reflection for dynamically getting the name of the property, but IMHO, it is important to balance your thoughts. In some cases, the performance overhead is not that critical, and you could benefit from some kind on strongly typed event firing mechanism.

因此,当然,在触发 PropertyChanged 时使用硬编码属性名称比使用反射动态获取属性名称更有效,但恕我直言,平衡您的想法很重要。在某些情况下,性能开销并不那么重要,您可以从某种强类型事件触发机制中受益。

Here is what I use sometimes in C# 3.0, when performances would not be a concern :

这是我有时在 C# 3.0 中使用的,当性能不是问题时:

public class Person : INotifyPropertyChanged
{
    private string name;

    public string Name
    {
        get { return this.name; }
        set 
        { 
            this.name = value;
            FirePropertyChanged(p => p.Name);
        }
    }

    private void FirePropertyChanged<TValue>(Expression<Func<Person, TValue>> propertySelector)
    {
        if (PropertyChanged == null)
            return;

        var memberExpression = propertySelector.Body as MemberExpression;
        if (memberExpression == null)
            return;

        PropertyChanged(this, new PropertyChangedEventArgs(memberExpression.Member.Name));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Notice the use of the expression tree to get the name of the property, and the use of the lambda expression as an Expression:

请注意使用表达式树来获取属性的名称,以及使用 lambda 表达式作为Expression

FirePropertyChanged(p => p.Name);

回答by Denys Wessels

In .NET 4.5 (C# 5.0) there is a new attribute called - CallerMemberNameit helps avoid hardcoded property names preventing the onset of bugs if developers decide to change a property name, here's an example:

在 .NET 4.5 (C# 5.0) 中有一个名为CallerMemberName的新属性,它有助于避免硬编码的属性名称,如果开发人员决定更改属性名称,则可以防止出现错误,这是一个示例:

public event PropertyChangedEventHandler PropertyChanged = delegate { };

public void OnPropertyChanged([CallerMemberName]string propertyName="")
{
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

private string name;
public string Name
{
    get { return name; }
    set 
    { 
        name = value;
        OnPropertyChanged();
    }
}

回答by Orion Adrian

The reflection overhead here is pretty much overkill especially since INotifyPropertyChanged gets called a lot. It's best just to hard code the value if you can.

这里的反射开销非常大,尤其是因为 INotifyPropertyChanged 被调用了很多。如果可以,最好只是对值进行硬编码。

If you aren't concerned about performance then I'd look at the various approached mentioned below and pick that that requires the least amount of coding. If you could do something to completely removes the need for the explicit call then that would be best (e.g. AOP).

如果您不关心性能,那么我会查看下面提到的各种方法并选择需要最少编码的方法。如果您可以做一些事情来完全消除对显式调用的需求,那将是最好的(例如 AOP)。

回答by Phil

The performance hit involved in the use of expression trees is due to the repeated resolution of the expression tree.

使用表达式树所涉及的性能损失是由于表达式树的重复解析。

The following code still uses expression trees and thus has the consequent advantages of being refactoring friendly and obfuscation friendly, but is actually approx 40% faster (very rough tests) than the usual technique - which consists of newing up a PropertyChangedEventArgs object for every change notification.

以下代码仍然使用表达式树,因此具有重构友好和混淆友好的后续优势,但实际上比通常的技术快约 40%(非常粗略的测试)——包括为每个更改通知更新一个 PropertyChangedEventArgs 对象.

It's quicker and avoids the performance hit of the expression tree because we cache a static PropertyChangedEventArgs object for each property.

因为我们为每个属性缓存了一个静态 PropertyChangedEventArgs 对象,所以它更快并避免了表达式树的性能下降。

There's one thing which I'm not yet doing - I intend to add some code which checks for debug builds that the property name for the supplied PropertChangedEventArgs object matches the property within which it is being used - at the moment with this code it is still possible for the developer to supply the wrong object.

有一件事我还没有做 - 我打算添加一些代码来检查调试版本,以确保所提供的 PropertChangedEventArgs 对象的属性名称与它正在使用的属性相匹配 - 目前使用此代码它仍然是开发人员可能提供错误的对象。

Check it out:

一探究竟:

    public class Observable<T> : INotifyPropertyChanged
    where T : Observable<T>
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected static PropertyChangedEventArgs CreateArgs(
        Expression<Func<T, object>> propertyExpression)
    {
        var lambda = propertyExpression as LambdaExpression;
        MemberExpression memberExpression;
        if (lambda.Body is UnaryExpression)
        {
            var unaryExpression = lambda.Body as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = lambda.Body as MemberExpression;
        }

        var propertyInfo = memberExpression.Member as PropertyInfo;

        return new PropertyChangedEventArgs(propertyInfo.Name);
    }

    protected void NotifyChange(PropertyChangedEventArgs args)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, args);
        }
    }
}

public class Person : Observable<Person>
{
    // property change event arg objects
    static PropertyChangedEventArgs _firstNameChangeArgs = CreateArgs(x => x.FirstName);
    static PropertyChangedEventArgs _lastNameChangeArgs = CreateArgs(x => x.LastName);

    string _firstName;
    string _lastName;

    public string FirstName
    {
        get { return _firstName; }
        set
        {
            _firstName = value;
            NotifyChange(_firstNameChangeArgs);
        }
    }

    public string LastName
    {
        get { return _lastName; }
        set
        {
            _lastName = value;
            NotifyChange(_lastNameChangeArgs);
        }
    }
}

回答by Philipp

Roman:

罗马:

I'd say you wouldn't even need the "Person" parameter - accordingly, a completely generic snippet like the one below should do:

我会说你甚至不需要“Person”参数——因此,像下面这样的一个完全通用的片段应该做:

private int age;
public int Age
{
  get { return age; }
  set
  {
    age = value;
    OnPropertyChanged(() => Age);
  }
}


private void OnPropertyChanged<T>(Expression<Func<T>> exp)
{
  //the cast will always succeed
  MemberExpression memberExpression = (MemberExpression) exp.Body;
  string propertyName = memberExpression.Member.Name;

  if (PropertyChanged != null)
  {
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  }
}

...however, I prefer sticking to string parameters with conditional validation in Debug builds. Josh Smith posted a nice sample on this:

...但是,我更喜欢在调试版本中坚持使用带有条件验证的字符串参数。乔什·史密斯 (Josh Smith) 发布了一个很好的示例:

A base class which implements INotifyPropertyChanged

实现 INotifyPropertyChanged 的​​基类

Cheers :) Philipp

干杯 :) 菲利普

回答by Jobi Joy

Yeah I see the use and simplicity of the function you are suggesting, but when considering the running cost due to reflection, yeah that is a bad idea, What I use for this scenario is having a Code snippetadded properly to take advantage of the time and error in writing a property with all the Notifyproperty event firing.

是的,我看到了您建议的功能的使用和简单性,但是在考虑由于反射而导致的运行成本时,是的,这是一个坏主意,我在这种情况下使用的是正确添加代码片段以利用时间并且在触发所有 Notifyproperty 事件时写入属性时出错。

回答by Peter Gfader

Another VERY NICE method I can think of is

我能想到的另一个非常好的方法是

Auto-implement INotifyPropertyChanged with Aspects
AOP: Aspect oriented programming

使用方面
AOP自动实现 INotifyPropertyChanged:面向方面的编程

Nice article on codeproject: AOP Implementation of INotifyPropertyChanged

关于代码项目的好文章:INotifyPropertyChanged 的​​ AOP 实现

回答by jbe

You might be interessted in this discussion about

你可能对这个讨论感兴趣

"Best Practices: How to implement INotifyPropertyChanged right?"

“最佳实践:如何正确实施 INotifyPropertyChanged?”

too.

也。

回答by Rekshino

Since C# 6.0 there is a nameof()keyword it will be evaluated at compile time, so it will has the performance as hardcoded value and is protected against mismatch with notified property.

由于 C# 6.0 有一个nameof()关键字,它将在编译时进行评估,因此它将具有硬编码值的性能,并防止与通知属性不匹配。

public event PropertyChangedEventHandler PropertyChanged;

protected void NotifyPropertyChanged(string info)
{       
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
public string SelectedItem
{
    get
    {
        return _selectedItem;
    }
    set
    {
        if (_selectedItem != value)
        {
            _selectedItem = value;
            NotifyPropertyChanged(nameof(SelectedItem));
        }
    }
}
private string _selectedItem;

回答by Kévin Rapaille

Without be irrevelant, between Hardcode and reflection, my choice is : notifypropertyweaver.

毫无疑问,在硬编码和反射之间,我的选择是:notifypropertyweaver

This Visual Studio package allow you to have the benefits of reflection (maintainability, readability,..) without have to lose perfs.

这个 Visual Studio 包允许您在不丢失性能的情况下获得反射的好处(可维护性、可读性等)。

Actually, you just have to implement the INotifyPropertyChanged and it add all the "notification stuff" at the compilation.

实际上,您只需要实现 INotifyPropertyChanged 并在编译时添加所有“通知内容”。

This is also fully parametrable if you want to fully optimize your code.

如果您想完全优化代码,这也是完全可参数化的。

For example, with notifypropertyweaver, you will have this code in you editor :

例如,使用notifypropertyweaver,您将在编辑器中包含以下代码:

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public string GivenNames { get; set; }
    public string FamilyName { get; set; }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }
}

Instead of :

代替 :

public class Person : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    private string givenNames;
    public string GivenNames
    {
        get { return givenNames; }
        set
        {
            if (value != givenNames)
            {
                givenNames = value;
                OnPropertyChanged("GivenNames");
                OnPropertyChanged("FullName");
            }
        }
    }

    private string familyName;
    public string FamilyName
    {
        get { return familyName; }
        set
        {
            if (value != familyName)
            {
                familyName = value;
                OnPropertyChanged("FamilyName");
                OnPropertyChanged("FullName");
            }
        }
    }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }

    public virtual void OnPropertyChanged(string propertyName)
    {
        var propertyChanged = PropertyChanged;
        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

For french speakers : Améliorez la lisibilité de votre code et simplifiez vous la vie avec notifypropertyweaver

对于法语使用者:Améliorez la lisibilité de votre code et simplifiez vous la vie avec notifypropertyweaver