微软应该为INotifyPropertyChanged实现一些时髦的东西,就像在自动属性中,只需要指定{get;设置;通知;} 我认为这样做很有意义。或者做这个手术有什么并发症吗?
我们能在属性中实现类似notify的东西吗。在你的类中实现INotifyPropertyChanged是否有一个优雅的解决方案,或者唯一的方法是在每个属性中引发PropertyChanged事件。
如果不是,我们可以写一些东西来自动生成一段代码来引发PropertyChanged事件?
微软应该为INotifyPropertyChanged实现一些时髦的东西,就像在自动属性中,只需要指定{get;设置;通知;} 我认为这样做很有意义。或者做这个手术有什么并发症吗?
我们能在属性中实现类似notify的东西吗。在你的类中实现INotifyPropertyChanged是否有一个优雅的解决方案,或者唯一的方法是在每个属性中引发PropertyChanged事件。
如果不是,我们可以写一些东西来自动生成一段代码来引发PropertyChanged事件?
当前回答
这就是大规模的过度工程。这比仅仅以正确的方式进行要复杂得多,而且几乎没有任何好处。如果你的IDE支持代码片段(Visual Studio/MonoDevelop支持),那么你可以让这个实现变得非常简单。你需要输入的只是属性的类型和属性名。额外的三行代码将自动生成。
其他回答
我真的很喜欢Marc的解决方案,但我认为可以稍微改进一下,以避免使用“魔法字符串”(不支持重构)。与其将属性名作为字符串使用,不如将其创建为lambda表达式:
private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, () => Name); }
}
只需要在Marc的代码中添加以下方法,就可以了:
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
if (selectorExpression == null)
throw new ArgumentNullException("selectorExpression");
MemberExpression body = selectorExpression.Body as MemberExpression;
if (body == null)
throw new ArgumentException("The body must be a member expression");
OnPropertyChanged(body.Member.Name);
}
protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(selectorExpression);
return true;
}
顺便说一句,这是受到这篇博客文章的启发。
让我介绍一下我自己的方法,叫做Yappi。 它属于运行时代理|派生类生成器,向现有对象或类型添加新功能,如种姓项目的动态代理。
它允许在基类中实现INotifyPropertyChanged一次,然后以以下风格声明派生类,仍然支持INotifyPropertyChanged用于新属性:
public class Animal:Concept
{
protected Animal(){}
public virtual string Name { get; set; }
public virtual int Age { get; set; }
}
派生类或代理构造的复杂性可以隐藏在下面这行代码后面:
var animal = Concept.Create<Animal>.New();
所有的INotifyPropertyChanged实现工作可以像这样完成:
public class Concept:INotifyPropertyChanged
{
//Hide constructor
protected Concept(){}
public static class Create<TConcept> where TConcept:Concept
{
//Construct derived Type calling PropertyProxy.ConstructType
public static readonly Type Type = PropertyProxy.ConstructType<TConcept, Implementation<TConcept>>(new Type[0], true);
//Create constructing delegate calling Constructor.Compile
public static Func<TConcept> New = Constructor.Compile<Func<TConcept>>(Type);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
{
var caller = PropertyChanged;
if(caller!=null)
{
caller(this, eventArgs);
}
}
//define implementation
public class Implementation<TConcept> : DefaultImplementation<TConcept> where TConcept:Concept
{
public override Func<TBaseType, TResult> OverrideGetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
{
return PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(property.Name);
}
/// <summary>
/// Overriding property setter implementation.
/// </summary>
/// <typeparam name="TBaseType">Base type for implementation. TBaseType must be TConcept, and inherits all its constraints. Also TBaseType is TDeclaringType.</typeparam>
/// <typeparam name="TDeclaringType">Type, declaring property.</typeparam>
/// <typeparam name="TConstructedType">Constructed type. TConstructedType is TDeclaringType and TBaseType.</typeparam>
/// <typeparam name="TResult">Type of property.</typeparam>
/// <param name="property">PropertyInfo of property.</param>
/// <returns>Delegate, corresponding to property setter implementation.</returns>
public override Action<TBaseType, TResult> OverrideSetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
{
//This code called once for each declared property on derived type's initialization.
//EventArgs instance is shared between all events for each concrete property.
var eventArgs = new PropertyChangedEventArgs(property.Name);
//get delegates for base calls.
Action<TBaseType, TResult> setter = PropertyImplementation<TBaseType, TDeclaringType>.GetSetter<TResult>(property.Name);
Func<TBaseType, TResult> getter = PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(property.Name);
var comparer = EqualityComparer<TResult>.Default;
return (pthis, value) =>
{//This code executes each time property setter is called.
if (comparer.Equals(value, getter(pthis))) return;
//base. call
setter(pthis, value);
//Directly accessing Concept's protected method.
pthis.OnPropertyChanged(eventArgs);
};
}
}
}
它对于重构是完全安全的,在类型构造后不使用反射,并且足够快。
我正在编写一个处理INotifyPropertyChanged的库,主要思想是使用动态代理来通知更改。
回购在这里:CaulyKan/NoMorePropertyChanged
使用这个库,你可以写:
public dynamic Test1Binding { get; set; }
public TestDTO Test1
{
get { return (TestDTO)Test1Binding; }
set { SetBinding(nameof(Test1Binding), value); }
}
然后将所有绑定和修改转到Test1Binding,无论TestDTO有多复杂,它都会自动通知PropertyChange和CollectionChanged。
它还可以处理依赖关系。
[DependsOn("Test1Binding.TestString")]
public string Test2
{
get { return Test1Binding.TestString; }
}
请给我一些建议。
我写了一篇文章来帮助你(https://msdn.microsoft.com/magazine/mt736453)。你可以使用SolSoft。DataBinding NuGet包。然后你可以这样写代码:
public class TestViewModel : IRaisePropertyChanged
{
public TestViewModel()
{
this.m_nameProperty = new NotifyProperty<string>(this, nameof(Name), null);
}
private readonly NotifyProperty<string> m_nameProperty;
public string Name
{
get
{
return m_nameProperty.Value;
}
set
{
m_nameProperty.SetValue(value);
}
}
// Plus implement IRaisePropertyChanged (or extend BaseViewModel)
}
好处:
基类是可选的 没有对每个“设定值”进行反思 可以具有依赖于其他属性的属性,并且它们都自动引发适当的事件(本文有一个这样的示例)
看这里:http://dotnet-forum.de/blogs/thearchitect/archive/2012/11/01/die-optimale-implementierung-des-inotifypropertychanged-interfaces.aspx
它是用德语写的,但你可以下载ViewModelBase.cs。cs-File中的所有注释都是用英语写的。
使用这个ViewModelBase-Class,可以实现类似于众所周知的依赖属性的可绑定属性:
public string SomeProperty
{
get { return GetValue( () => SomeProperty ); }
set { SetValue( () => SomeProperty, value ); }
}