Tim*_*ell 57 c# inotifypropertychanged
有没有办法自动获得类中属性更改的通知,而无需在每个setter中编写OnPropertyChanged?(如果有更改,我有数百个我想知道的属性).
安东建议使用动态代理.我实际上使用过"Castle"库来获得类似的东西,虽然它确实减少了我必须编写的代码量,但它增加了大约30秒到我的程序启动时间(ymmv) - 因为它是一个运行时方案.
我想知道是否有编译时解决方案,可能使用编译时属性...
Slashene和TcKs给出了产生重复代码的建议 - 遗憾的是,并非所有属性都是m_Value = value的简单情况 - 很多都在setter中有自定义代码,所以来自代码片段和xml的cookie-cutter代码实际上不可行我的项目也是.
Isa*_*avo 43
编辑: NotifyPropertyWeaver的作者已弃用该工具,转而支持更一般的Fody.(有关人员从织女到动物的迁移指南可供使用.)
我用于项目的一个非常方便的工具是Notify Property Weaver Fody.
它将自身安装为项目中的构建步骤,并在编译期间注入引发PropertyChanged事件的代码.
使属性提升PropertyChanged是通过在它们上面添加特殊属性来完成的:
[ImplementPropertyChanged]
public string MyProperty { get; set; }
Run Code Online (Sandbox Code Playgroud)
作为奖励,您还可以指定依赖于其他属性的属性的关系
[ImplementPropertyChanged]
public double Radius { get; set; }
[DependsOn("Radius")]
public double Area
{
get { return Radius * Radius * Math.PI; }
}
Run Code Online (Sandbox Code Playgroud)
Svi*_*ish 39
我们使用下面的代码(来自http://www.ingebrigtsen.info/post/2008/12/11/INotifyPropertyChanged-revisited.aspx).很棒:)
public static class NotificationExtensions
{
#region Delegates
/// <summary>
/// A property changed handler without the property name.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sender">The object that raised the event.</param>
public delegate void PropertyChangedHandler<TSender>(TSender sender);
#endregion
/// <summary>
/// Notifies listeners about a change.
/// </summary>
/// <param name="EventHandler">The event to raise.</param>
/// <param name="Property">The property that changed.</param>
public static void Notify(this PropertyChangedEventHandler EventHandler, Expression<Func<object>> Property)
{
// Check for null
if (EventHandler == null)
return;
// Get property name
var lambda = Property 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;
}
ConstantExpression constantExpression;
if (memberExpression.Expression is UnaryExpression)
{
var unaryExpression = memberExpression.Expression as UnaryExpression;
constantExpression = unaryExpression.Operand as ConstantExpression;
}
else
{
constantExpression = memberExpression.Expression as ConstantExpression;
}
var propertyInfo = memberExpression.Member as PropertyInfo;
// Invoke event
foreach (Delegate del in EventHandler.GetInvocationList())
{
del.DynamicInvoke(new[]
{
constantExpression.Value, new PropertyChangedEventArgs(propertyInfo.Name)
});
}
}
/// <summary>
/// Subscribe to changes in an object implementing INotifiyPropertyChanged.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="ObjectThatNotifies">The object you are interested in.</param>
/// <param name="Property">The property you are interested in.</param>
/// <param name="Handler">The delegate that will handle the event.</param>
public static void SubscribeToChange<T>(this T ObjectThatNotifies, Expression<Func<object>> Property, PropertyChangedHandler<T> Handler) where T : INotifyPropertyChanged
{
// Add a new PropertyChangedEventHandler
ObjectThatNotifies.PropertyChanged += (s, e) =>
{
// Get name of Property
var lambda = Property 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;
// Notify handler if PropertyName is the one we were interested in
if (e.PropertyName.Equals(propertyInfo.Name))
{
Handler(ObjectThatNotifies);
}
};
}
}
Run Code Online (Sandbox Code Playgroud)
例如这样使用:
public class Employee : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _firstName;
public string FirstName
{
get { return this._firstName; }
set
{
this._firstName = value;
this.PropertyChanged.Notify(()=>this.FirstName);
}
}
}
private void firstName_PropertyChanged(Employee sender)
{
Console.WriteLine(sender.FirstName);
}
employee = new Employee();
employee.SubscribeToChange(() => employee.FirstName, firstName_PropertyChanged);
Run Code Online (Sandbox Code Playgroud)
示例中可能存在一些语法错误.没试过.但你应该至少有这个概念:)
编辑:我现在看到你可能想要更少的工作,但是是的...上面的东西至少使它变得容易多了.并且使用字符串引用属性可以防止所有可怕的问题.
tak*_*krl 31
Framework 4.5为我们提供了CallerMemberNameAttribute,它使得将属性名称作为字符串传递是不必要的:
private string m_myProperty;
public string MyProperty
{
get { return m_myProperty; }
set
{
m_myProperty = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
// ... do stuff here ...
}
Run Code Online (Sandbox Code Playgroud)
类似于Svish的解决方案,只需用无聊的框架功能取代lambda awesomeness ;-)
如果您正在使用安装了KB2468871的 Framework 4.0,则可以通过nuget安装Microsoft BCL兼容包,它还提供此属性.
Kon*_*rin 11
您可以在PropertyChanged委托上使用扩展方法并像这样使用它:
public string Name
{
get { return name; }
set
{
name = value;
PropertyChanged.Raise(() => Name);
}
}
Run Code Online (Sandbox Code Playgroud)
订阅特定房产变更:
var obj = new Employee();
var handler = obj.SubscribeToPropertyChanged(
o => o.FirstName,
o => Console.WriteLine("FirstName is now '{0}'", o.FirstName));
obj.FirstName = "abc";
// Unsubscribe when required
obj.PropertyChanged -= handler;
Run Code Online (Sandbox Code Playgroud)
扩展方法只能通过检查lambda表达式树来确定发送方和属性名称,而不会对性能产生重大影响:
public static class PropertyChangedExtensions
{
public static void Raise<TProperty>(
this PropertyChangedEventHandler handler, Expression<Func<TProperty>> property)
{
if (handler == null)
return;
var memberExpr = (MemberExpression)property.Body;
var propertyName = memberExpr.Member.Name;
var sender = ((ConstantExpression)memberExpr.Expression).Value;
handler.Invoke(sender, new PropertyChangedEventArgs(propertyName));
}
public static PropertyChangedEventHandler SubscribeToPropertyChanged<T, TProperty>(
this T obj, Expression<Func<T, TProperty>> property, Action<T> handler)
where T : INotifyPropertyChanged
{
if (handler == null)
return null;
var memberExpr = (MemberExpression)property.Body;
var propertyName = memberExpr.Member.Name;
PropertyChangedEventHandler subscription = (sender, eventArgs) =>
{
if (propertyName == eventArgs.PropertyName)
handler(obj);
};
obj.PropertyChanged += subscription;
return subscription;
}
}
Run Code Online (Sandbox Code Playgroud)
如果PropertyChanged在基类型中声明事件,则它将不会作为派生类中的委托字段可见.在这种情况下,解决方法是声明类型的受保护字段PropertyChangedEventHandler并显式实现事件add和remove访问器:
public class Base : INotifyPropertyChanged
{
protected PropertyChangedEventHandler propertyChanged;
public event PropertyChangedEventHandler PropertyChanged
{
add { propertyChanged += value; }
remove { propertyChanged -= value; }
}
}
public class Derived : Base
{
string name;
public string Name
{
get { return name; }
set
{
name = value;
propertyChanged.Raise(() => Name);
}
}
}
Run Code Online (Sandbox Code Playgroud)
Nic*_*ier 10
实现类型安全INotifyPropertyChanged:请参见此处
然后制作自己的代码段:
private $Type$ _$PropertyName$;
public $Type$ $PropertyName$
{
get
{
return _$PropertyName$;
}
set
{
if(value != _$PropertyName$)
{
_$PropertyName$ = value;
OnPropertyChanged(o => o.$PropertyName$);
}
}
}
Run Code Online (Sandbox Code Playgroud)
使用Code代码段设计器,您已经完成了!简单,安全的方式来建立您的INotifyPropertyChanged.
我不知道没有标准方法,但我知道两种解决方法:
1)编译后PostSharp可以为您完成。它非常有用,但每次构建都需要一些时间。
2) Visual Studio 中的自定义工具。您可以将它与“部分类”结合起来。然后,您可以为 XML 创建自定义工具,并可以从 xml 生成源代码。
例如这个 xml:
<type scope="public" type="class" name="MyClass">
<property scope="public" type="string" modifier="virtual" name="Text" notify="true" />
</type>
Run Code Online (Sandbox Code Playgroud)
可以是此代码的来源:
public partial class MyClass {
private string _text;
public virtual string Text {
get { return this._Text; }
set {
this.OnPropertyChanging( "Text" );
this._Text = value;
this.OnPropertyChanged( "Text" );
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
25852 次 |
| 最近记录: |