Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net INotifyPropertyChanged属性名称-硬代码与反射?_.net_Wpf_Reflection - Fatal编程技术网

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

.net INotifyPropertyChanged属性名称-硬代码与反射?,.net,wpf,reflection,.net,Wpf,Reflection,使用INotifyPropertyChanged时,指定属性名称的最佳方法是什么 大多数示例将属性名称硬编码为PropertyChanged事件的参数。我曾考虑使用MethodBase.GetCurrentMethod.Name.Substring(4),但对反射开销感到有点不安。这里的反射开销太大了,特别是因为INotifyPropertyChanged经常被调用。如果可以,最好只是硬编码值 如果您不关心性能,那么我会看看下面提到的各种方法,选择需要最少编码的方法。如果您可以做一些事情来完全消

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


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

这里的反射开销太大了,特别是因为INotifyPropertyChanged经常被调用。如果可以,最好只是硬编码值


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

是的,我看到了您建议的函数的使用和简单性,但是考虑到反射的运行成本,是的,这是个坏主意,我在这个场景中使用的是正确添加一个代码片段,以利用在所有Notifyproperty事件触发时写入属性的时间和错误。

此外,我们发现了一个问题,即在调试版本和发布版本中获取方法名的工作方式不同:


(我们使用的代码并不像您建议的那样完全是反射,但它让我们相信,对属性名称进行硬编码是最快、最可靠的解决方案。)

我做了一次这样的实验,从内存中看,它工作正常,并且不再需要对字符串中的所有属性名称进行硬编码。如果您在桌面上构建一个大容量服务器应用程序,那么性能可能会有问题,您可能永远不会注意到这一差异

protected void OnPropertyChanged()
{
    OnPropertyChanged(PropertyName);
}

protected string PropertyName
{
    get
    {
        MethodBase mb = new StackFrame(1).GetMethod();
        string name = mb.Name;
        if(mb.Name.IndexOf("get_") > -1)
            name = mb.Name.Replace("get_", "");

        if(mb.Name.IndexOf("set_") > -1)
            name = mb.Name.Replace("set_", "");

        return name;
    }
}

基于反射的方法的问题是它相当昂贵,而且速度也不是很快。当然,它更灵活,在重构方面也不那么脆弱

但是,它确实会影响性能,尤其是在频繁调用对象时。stackframe方法(我相信)在CAS中也存在问题(例如,受限制的信任级别,如XBAP)。最好是硬编码


如果您在WPF中寻找快速、灵活的属性通知,有一个解决方案——使用DependencyObject:),这就是它的设计目的。如果您不想接受依赖关系,或者担心线程亲和性问题,请将属性名称移动到常量中,然后爆炸!很好。

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

最明显的例子是数据绑定

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

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

以下是我在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;
}
罗马人:

我想说,您甚至不需要“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));
  }
}
privateintage;
公共信息
{
获取{返回年龄;}
设置
{
年龄=价值;
房地产变化(()=>年龄);
}
}
私有void OnPropertyChanged(表达式exp)
{
//演员阵容将永远成功
MemberExpression MemberExpression=(MemberExpression)exp.Body;
字符串propertyName=memberExpression.Member.Name;
if(PropertyChanged!=null)
{
PropertyChanged(这是新的PropertyChangedEventArgs(propertyName));
}
}
…但是,我更喜欢在调试构建中使用条件验证来坚持字符串参数。Josh Smith在上面发布了一个很好的示例:

干杯:)
Philipp

您可能希望避免完全改变InotifyProperty。它会将不必要的簿记代码添加到项目中。请考虑使用。

你可能会在这个关于

的讨论中遇到麻烦。


也是。

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

自动实现InotifyProperty随方面更改
AOP:面向方面编程


关于codeproject的文章不错:

表达式树的使用对性能的影响是由于表达式树的重复解析

下面的代码仍然使用表达式树,因此具有重构友好和模糊友好的优点,但实际上比通常的技术快约40%(非常粗略的测试),其中包括为每个更改通知更新PropertyChangedEventArgs对象

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

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

请查看:

    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);
        }
    }
}
可观察的公共类:INotifyPropertyChanged
式中T:可观测
    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);
        }
    }
}
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);
        }
    }
}
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));
        }
    }
}
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();
    }
}
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;