微软应该为INotifyPropertyChanged实现一些时髦的东西,就像在自动属性中,只需要指定{get;设置;通知;} 我认为这样做很有意义。或者做这个手术有什么并发症吗?
我们能在属性中实现类似notify的东西吗。在你的类中实现INotifyPropertyChanged是否有一个优雅的解决方案,或者唯一的方法是在每个属性中引发PropertyChanged事件。
如果不是,我们可以写一些东西来自动生成一段代码来引发PropertyChanged事件?
微软应该为INotifyPropertyChanged实现一些时髦的东西,就像在自动属性中,只需要指定{get;设置;通知;} 我认为这样做很有意义。或者做这个手术有什么并发症吗?
我们能在属性中实现类似notify的东西吗。在你的类中实现INotifyPropertyChanged是否有一个优雅的解决方案,或者唯一的方法是在每个属性中引发PropertyChanged事件。
如果不是,我们可以写一些东西来自动生成一段代码来引发PropertyChanged事件?
当前回答
我刚刚发现ActiveSharp - Automatic INotifyPropertyChanged,我还没有使用它,但它看起来不错。
从它的网站上引用…
发送属性更改通知 没有指定属性名为 字符串。
相反,可以这样写属性:
public int Foo
{
get { return _foo; }
set { SetValue(ref _foo, value); } // <-- no property name here
}
注意,不需要将属性的名称作为字符串包含。ActiveSharp可靠而正确地为自己计算出来。它的工作基于这样一个事实,即你的属性实现通过ref传递支持字段(_foo) (ActiveSharp使用“by ref”调用来识别哪个支持字段被传递,并从字段标识属性)。
其他回答
我使用以下扩展方法(使用c# 6.0)使INPC实现尽可能简单:
public static bool ChangeProperty<T>(this PropertyChangedEventHandler propertyChanged, ref T field, T value, object sender,
IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
{
if (comparer == null)
comparer = EqualityComparer<T>.Default;
if (comparer.Equals(field, value))
{
return false;
}
else
{
field = value;
propertyChanged?.Invoke(sender, new PropertyChangedEventArgs(propertyName));
return true;
}
}
INPC实现可以归结为(你可以每次都实现它,也可以创建一个基类):
public class INPCBaseClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool changeProperty<T>(ref T field, T value,
IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
{
return PropertyChanged.ChangeProperty(ref field, value, this, comparer, propertyName);
}
}
然后像这样写属性:
private string testProperty;
public string TestProperty
{
get { return testProperty; }
set { changeProperty(ref testProperty, value); }
}
注意:如果您愿意,可以在扩展方法中省略[CallerMemberName]声明,但我想保持它的灵活性。
如果你有一个没有支持字段的属性,你可以重载changeProperty:
protected bool changeProperty<T>(T property, Action<T> set, T value,
IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
{
bool ret = changeProperty(ref property, value, comparer, propertyName);
if (ret)
set(property);
return ret;
}
一个例子是:
public string MyTestProperty
{
get { return base.TestProperty; }
set { changeProperty(base.TestProperty, (x) => { base.TestProperty = x; }, value); }
}
让我介绍一下我自己的方法,叫做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);
};
}
}
}
它对于重构是完全安全的,在类型构造后不使用反射,并且足够快。
所有这些答案都很好。
我的解决方案是使用代码片段来完成这项工作。
这使用了对PropertyChanged事件最简单的调用。
保存此代码段并像使用“fullprop”代码段一样使用它。
位置可以在'工具\代码片段管理器…的菜单。
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>inotifypropfull</Title>
<Shortcut>inotifypropfull</Shortcut>
<HelpUrl>http://ofirzeitoun.wordpress.com/</HelpUrl>
<Description>Code snippet for property and backing field with notification</Description>
<Author>Ofir Zeitoun</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>type</ID>
<ToolTip>Property type</ToolTip>
<Default>int</Default>
</Literal>
<Literal>
<ID>property</ID>
<ToolTip>Property name</ToolTip>
<Default>MyProperty</Default>
</Literal>
<Literal>
<ID>field</ID>
<ToolTip>The variable backing this property</ToolTip>
<Default>myVar</Default>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[private $type$ $field$;
public $type$ $property$
{
get { return $field$;}
set {
$field$ = value;
var temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs("$property$"));
}
}
}
$end$]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
您可以根据需要修改调用(使用上述解决方案)
还有Fody,它有一个AddINotifyPropertyChangedInterface插件,它让你这样写:
[AddINotifyPropertyChangedInterface]
public class Person
{
public string GivenNames { get; set; }
public string FamilyName { get; set; }
}
...并在编译时注入属性更改通知。
另一个组合解决方案是使用StackFrame:
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Set<T>(ref T field, T value)
{
MethodBase method = new StackFrame(1).GetMethod();
field = value;
Raise(method.Name.Substring(4));
}
protected void Raise(string propertyName)
{
var temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs(propertyName));
}
}
}
用法:
public class TempVM : BaseViewModel
{
private int _intP;
public int IntP
{
get { return _intP; }
set { Set<int>(ref _intP, value); }
}
}