你好!
这里我有一个简单的类示例,其中包含三个类型为B类的字段以及其他一些内容。 正如你所看到我正在监听每个孩子对象的变化。 因为我可能需要很多类型为B的属性,我想知道是否有一种缩小代码的方法。为每个人创建一个监听器+一个方法似乎我会有很多代码。我如何解决这个...使用字典或类似的东西?我被告知IoC可以解决这个问题,但我不确定从哪里开始。
public class A : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int _id;
public int Id
{
get { return _id; }
set
{
if (_id == value)
{
return;
}
_id = value;
OnPropertyChanged("Id");
}
}
public string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value)
{
return;
}
_name = value;
OnPropertyChanged("Name");
}
}
public B _firstB;
public B FirstB
{
get { return _firstB; }
set
{
if (_firstB == value)
{
return;
}
if (_firstB != null)
{
FirstB.PropertyChanged -= firstObjectB_Listener;
}
_firstB = value;
if (_firstB != null)
FirstB.PropertyChanged += new PropertyChangedEventHandler(firstObjectB_Listener);
OnPropertyChanged("FirstB");
}
}
public B _secondB;
public B SecondB
{
get { return _secondB; }
set
{
if (_secondB == value)
{
return;
}
if (_secondB != null)
{
FirstB.PropertyChanged -= secondObjectB_Listener;
}
_secondB = value;
if (_secondB != null)
SecondB.PropertyChanged += new PropertyChangedEventHandler(secondObjectB_Listener);
OnPropertyChanged("FirstB");
}
}
public B _thirdB;
public B ThirdB
{
get { return _thirdB; }
set
{
if (_thirdB == value)
{
return;
}
if (_thirdB != null)
{
ThirdB.PropertyChanged -= thirdObjectB_Listener;
}
_thirdB = value;
if (_thirdB != null)
ThirdB.PropertyChanged += new PropertyChangedEventHandler(thirdObjectB_Listener);
OnPropertyChanged("ThirdB");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
void firstObjectB_Listener(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("Object A has found a change of " + e.PropertyName + " on first object B");
}
void secondObjectB_Listener(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("Object A has found a change of " + e.PropertyName + " on second object B");
}
void thirdObjectB_Listener(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("Object A has found a change of " + e.PropertyName + " on third object B");
}
}
答案 0 :(得分:2)
我所知道的最优雅的方法是使用面向方面编程(AOP)和PostSharp等工具。我找到了INotifyPropertyChanged实现示例here和here。这些允许您使用属性装饰属性,然后PostSharp在构建代码时为您实现INotifyPropertyChanged。
答案 1 :(得分:1)
看起来您正在设置依赖关系链。 AOP或静态分析解决方案都没有适当地处理这个问题。查看更新控件,它使用依赖关系跟踪到discover dependency chains at runtime。
以下是您的示例:
public class B
{
private Independent<string> _someProperty = new Independent<string>();
public string SomeProperty
{
get { return _someProperty; }
set { _someProperty.Value = value; }
}
}
public class A
{
private Dependent<string> _dependentProperty;
public A()
{
_dependentProperty = new Dependent<string>(() =>
FirstB.SomeProperty + ", " + SecondB.SomeProperty + ", " + ThirdB.SomeProperty);
}
public string DependentProperty
{
get { return _dependentProperty; }
}
private Independent<int> _id = new Independent<int>();
public int Id
{
get { return _id; }
set { _id.Value = value; }
}
private Independent<string> _name = new Independent<string>();
public string Name
{
get { return _name; }
set { _name.Value = value; }
}
private Independent<B> _firstB = new Independent<B>();
public B FirstB
{
get { return _firstB; }
set { _firstB.Value = value; }
}
private Independent<B> _secondB = new Independent<B>();
public B SecondB
{
get { return _secondB; }
set { _secondB.Value = value; }
}
private Independent<B> _thirdB = new Independent<B>();
public B ThirdB
{
get { return _thirdB; }
set { _thirdB.Value = value; }
}
}
答案 2 :(得分:0)
可以找到一种简化属性设置的好方法here。
关于您的级联通知:我猜您可以使用上面概述的方法来处理(非)订阅在那里实现INotifyPropertyChanged
的属性的事件。
答案 3 :(得分:0)
为了简化一点,你可以做以下两件事。
首先,在PropertyChanged的处理程序中,第一个参数sender是触发事件的对象,至少如果你在B类中以与A类相同的方式实现了OnPropertyChanged,这意味着你只需要一个所有B属性的处理程序。
private void BValueListener(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("Found change of {0} on object {1}", e.PropertyName, sender);
}
如果您需要确切知道发送哪个B属性,您可以在BValueListener方法中进行检查。
if (sender == FirstB) { /* Do special stuff here */ }
对于所有B属性具有相同的侦听器,我们可以继续编写属性setter,如:
private B _thirdB;
public B ThirdB
{
get { return _thirdB; }
set {
if (UpdateBValue(ref _thirdB, value)) {
OnPropertyChanged("ThirdB");
}
}
}
private bool UpdateBValue(ref B value, B newValue)
{
if (value == newValue)
{
return false;
}
if (value != null)
{
value.PropertyChanged -= BValueListener;
}
value = newValue;
if (value != null)
{
value.PropertyChanged += BValueListener;
}
return true;
}
如果你真的需要为每个属性使用不同的处理程序,你可以将上面的代码修改为类似
private B _thirdB;
public B ThirdB
{
get { return _thirdB; }
set
{
if (UpdateBValue(ref _thirdB, value, BValueListener))
{
OnPropertyChanged("ThirdB");
}
}
}
private bool UpdateBValue(ref B value, B newValue, PropertyChangedEventHandler eventHandler)
{
if (value == newValue)
{
return false;
}
if (value != null)
{
value.PropertyChanged -= eventHandler;
}
value = newValue;
if (value != null)
{
value.PropertyChanged += eventHandler;
}
return true;
}
您可以在每种情况下发送您想要使用的侦听器方法。
答案 4 :(得分:0)
您可以考虑使用的工具是T4(T4随VS2010一起提供,因此不需要额外的依赖项。)
T4是一种代码生成工具,可以帮助减少“繁琐”代码的维护。想想ASP / PHP代码。它也类似于XML / XSLT。
有关T4的精彩介绍,请参阅Oleg Sychs博客。
在这种情况下代码生成的好处是,即使生成的代码是冗余的,您维护的代码(T4模板)也不会或至少不那么多余。
考虑一下你提供的样本,我写了这个T4模板: (如果要在Visual Studio中尝试此模板,请单击“添加新项”,选择类模板,但将扩展名从.cs更改为.tt,将以下源粘贴到.tt文件中并保存。保存后,结果应该在相应的.cs文件)
// ReSharper disable InconsistentNaming
// ReSharper disable PartialMethodWithSinglePart
// ReSharper disable PartialTypeWithSinglePart
<#
// This is the "model" that is "what" we would like to generate
var classDefs = new []
{
new ClassDefinition
{
Name = "A",
Properties = new []
{
P ("int" , "Id" ),
P ("string" , "Name" ),
P ("B" , "FirstB" , listenToChanges:true ),
P ("B" , "SecondB" , listenToChanges:true ),
P ("B" , "ThirdB" , listenToChanges:true ),
},
},
new ClassDefinition
{
Name = "B",
Properties = new []
{
P ("int" , "Id" ),
P ("string" , "Name" ),
},
},
};
#>
namespace SO
{
using System;
using System.ComponentModel;
<#
// This part is the template ie "how" the model will be transformed into code
foreach (var classDef in classDefs)
{
#>
// ------------------------------------------------------------------------
/// <summary>
/// class <#=classDef.Name#> (implements INotifyPropertyChanged)
/// </summary>
public partial class <#=classDef.Name#> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged (string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler (this, new PropertyChangedEventArgs (name));
}
}
<#
foreach (var propertyDef in classDef.Properties)
{
#>
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property <#=propertyDef.Name#> (<#=propertyDef.Type#>)
/// </summary>
public <#=propertyDef.Type#> <#=propertyDef.Name#>
{
get { return <#=propertyDef.FieldName#>; }
set
{
if (<#=propertyDef.FieldName#> == value)
{
return;
}
<#
if (propertyDef.ListenToChanges)
{
#>
if (<#=propertyDef.FieldName#> != null)
{
<#=propertyDef.FieldName#>.PropertyChanged -= <#=propertyDef.ListenerName#>;
}
<#=propertyDef.FieldName#> = value;
if (<#=propertyDef.FieldName#> != null)
{
<#=propertyDef.FieldName#>.PropertyChanged += <#=propertyDef.ListenerName#>;
}
<#
}
else
{
#>
<#=propertyDef.FieldName#> = value;
<#
}
#>
<#=propertyDef.EventName#> ();
OnPropertyChanged("<#=propertyDef.Name#>");
}
}
// --------------------------------------------------------------------
<#=propertyDef.Type#> <#=propertyDef.FieldName#>;
// --------------------------------------------------------------------
partial void <#=propertyDef.EventName#> ();
// --------------------------------------------------------------------
<#
if (propertyDef.ListenToChanges)
{
#>
void <#=propertyDef.ListenerName#> (object sender, PropertyChangedEventArgs e)
{
Console.WriteLine (
"Instance of <#=classDef.Name#> detected a change of <#=propertyDef.Name#>.{0}",
e.PropertyName
);
<#=propertyDef.EventName#> ();
}
// --------------------------------------------------------------------
<#
}
}
#>
}
// ------------------------------------------------------------------------
<#
}
#>
}
<#+
class ClassDefinition
{
public string Name;
public PropertyDefinition[] Properties;
}
class PropertyDefinition
{
public string Type;
public string Name;
public bool ListenToChanges;
public string FieldName
{
get
{
return "_" + Name;
}
}
public string ListenerName
{
get
{
return Name + "_Listener";
}
}
public string EventName
{
get
{
return "Change_" + Name;
}
}
}
PropertyDefinition P (string type, string name, bool listenToChanges = false)
{
return new PropertyDefinition
{
Type = type ?? "<NO_TYPE>",
Name = name ?? "<NO_NAME>",
ListenToChanges = listenToChanges,
};
}
#>
最后这会产生这个输出:
// ReSharper disable InconsistentNaming
// ReSharper disable PartialMethodWithSinglePart
// ReSharper disable PartialTypeWithSinglePart
namespace SO
{
using System;
using System.ComponentModel;
// ------------------------------------------------------------------------
/// <summary>
/// class A (implements INotifyPropertyChanged)
/// </summary>
public partial class A : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged (string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler (this, new PropertyChangedEventArgs (name));
}
}
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property Id (int)
/// </summary>
public int Id
{
get { return _Id; }
set
{
if (_Id == value)
{
return;
}
_Id = value;
Change_Id ();
OnPropertyChanged("Id");
}
}
// --------------------------------------------------------------------
int _Id;
// --------------------------------------------------------------------
partial void Change_Id ();
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property Name (string)
/// </summary>
public string Name
{
get { return _Name; }
set
{
if (_Name == value)
{
return;
}
_Name = value;
Change_Name ();
OnPropertyChanged("Name");
}
}
// --------------------------------------------------------------------
string _Name;
// --------------------------------------------------------------------
partial void Change_Name ();
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property FirstB (B)
/// </summary>
public B FirstB
{
get { return _FirstB; }
set
{
if (_FirstB == value)
{
return;
}
if (_FirstB != null)
{
_FirstB.PropertyChanged -= FirstB_Listener;
}
_FirstB = value;
if (_FirstB != null)
{
_FirstB.PropertyChanged += FirstB_Listener;
}
Change_FirstB ();
OnPropertyChanged("FirstB");
}
}
// --------------------------------------------------------------------
B _FirstB;
// --------------------------------------------------------------------
partial void Change_FirstB ();
// --------------------------------------------------------------------
void FirstB_Listener (object sender, PropertyChangedEventArgs e)
{
Console.WriteLine (
"Instance of A detected a change of FirstB.{0}",
e.PropertyName
);
Change_FirstB ();
}
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property SecondB (B)
/// </summary>
public B SecondB
{
get { return _SecondB; }
set
{
if (_SecondB == value)
{
return;
}
if (_SecondB != null)
{
_SecondB.PropertyChanged -= SecondB_Listener;
}
_SecondB = value;
if (_SecondB != null)
{
_SecondB.PropertyChanged += SecondB_Listener;
}
Change_SecondB ();
OnPropertyChanged("SecondB");
}
}
// --------------------------------------------------------------------
B _SecondB;
// --------------------------------------------------------------------
partial void Change_SecondB ();
// --------------------------------------------------------------------
void SecondB_Listener (object sender, PropertyChangedEventArgs e)
{
Console.WriteLine (
"Instance of A detected a change of SecondB.{0}",
e.PropertyName
);
Change_SecondB ();
}
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property ThirdB (B)
/// </summary>
public B ThirdB
{
get { return _ThirdB; }
set
{
if (_ThirdB == value)
{
return;
}
if (_ThirdB != null)
{
_ThirdB.PropertyChanged -= ThirdB_Listener;
}
_ThirdB = value;
if (_ThirdB != null)
{
_ThirdB.PropertyChanged += ThirdB_Listener;
}
Change_ThirdB ();
OnPropertyChanged("ThirdB");
}
}
// --------------------------------------------------------------------
B _ThirdB;
// --------------------------------------------------------------------
partial void Change_ThirdB ();
// --------------------------------------------------------------------
void ThirdB_Listener (object sender, PropertyChangedEventArgs e)
{
Console.WriteLine (
"Instance of A detected a change of ThirdB.{0}",
e.PropertyName
);
Change_ThirdB ();
}
// --------------------------------------------------------------------
}
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
/// <summary>
/// class B (implements INotifyPropertyChanged)
/// </summary>
public partial class B : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged (string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler (this, new PropertyChangedEventArgs (name));
}
}
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property Id (int)
/// </summary>
public int Id
{
get { return _Id; }
set
{
if (_Id == value)
{
return;
}
_Id = value;
Change_Id ();
OnPropertyChanged("Id");
}
}
// --------------------------------------------------------------------
int _Id;
// --------------------------------------------------------------------
partial void Change_Id ();
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property Name (string)
/// </summary>
public string Name
{
get { return _Name; }
set
{
if (_Name == value)
{
return;
}
_Name = value;
Change_Name ();
OnPropertyChanged("Name");
}
}
// --------------------------------------------------------------------
string _Name;
// --------------------------------------------------------------------
partial void Change_Name ();
// --------------------------------------------------------------------
}
// ------------------------------------------------------------------------
}