更优雅地处理属性更改事件侦听器(很多)(字典?)
你好 !
这里我有一个简单的类示例,其中包含三个 B 类类型的字段和一些其他内容。 正如你所看到的,我正在监听每个子对象的变化。 由于我可能需要 B 类类型的大量属性,我想知道是否有一种方法可以缩小代码。为每个创建一个侦听器+一个方法似乎我将有很多代码。我该如何解决这个问题......使用字典或类似的东西?我被告知国际奥委会可以解决这个问题,但我不知道从哪里开始。
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");
}
}
hELLO !
Here i have a simple class example with three fields of type class B and some other stuff.
As you can see im listening on every child object change.
Since i could need alot of properties of type class B i wonder if there is a way of shrinking the code. Creating a listener + a method for each seems like i will have ALOT of code. How would i fix this ... using a dictionary or something similar? I have been told that IoC could fix this, but im not sure where to start.
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");
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我所知道的最优雅的方法是将面向方面编程(AOP)与诸如 PostSharp 之类的工具一起使用。我在此处和此处。这些允许您使用属性来装饰您的属性,然后 PostSharp 在构建代码时为您实现 INotifyPropertyChanged。
The most elegant way I know of is to use Aspect Oriented Programming (AOP) with a tool such as PostSharp. I found INotifyPropertyChanged implementation examples here and here. These allow you to decorate your properties with an attribute and PostSharp then implements INotifyPropertyChanged for you when the code is built.
看起来您正在设置依赖链。 AOP 或静态分析解决方案都无法正确处理这个问题。查看 Update Controls,它使用依赖项跟踪来在运行时发现依赖项链。
你的例子变成了这样:
It looks like you are setting up a dependency chain. None of the AOP or static analysis solutions are going to handle this properly. Check out Update Controls, which uses dependency tracking to discover dependency chains at runtime.
Here's what your example becomes:
可以在 此处找到一种简化属性设置的好方法。
关于您的级联通知:我猜您可以使用上面概述的方法来处理那里实现
INotifyPropertyChanged
的属性的事件订阅(取消)订阅。One nice way to simplify the setup of your properties can be found here.
Regarding to your cascading notifications: I'd guess that you could use the approach outlined above to handle the (un-)subscription of events for properties implementing
INotifyPropertyChanged
there.为了简化一点,您可以执行以下两件事。
首先,在 PropertyChanged 的处理程序中,第一个参数 sender 是触发事件的对象,至少如果您在类 B 中以与在类 A 中相同的方式实现了 OnPropertyChanged 的话。这意味着您只需要一个处理程序来处理所有事件B 属性。
如果您需要确切知道发送的是哪个 B 属性,您可以在 BValueListener 方法中进行检查。
为所有 B 属性使用相同的侦听器,然后我们可以继续编写属性设置器,如下所示:
如果您确实需要为每个属性使用不同的处理程序,您可以将上面的代码修改为
可以在您想要的侦听器方法中发送的 位置在每种情况下使用。
To simplify at little bit you can do the following two things.
First, in the handler of the PropertyChanged the first parameter, sender, is the object that fired the event, at least if you have implmented the OnPropertyChanged in class B the same way as in class A. This means you only need one handler for all the B properties.
If you need to do know exactly which of the B properties did the sending you could do checks in the BValueListener method.
Having the same listener for all the B properties we can then move on to write the property setter like:
If you really need different handlers for each property you can modify the code above to something like
where you can send in the listener method you would like to use in each case.
您可以考虑的一个工具是 T4(T4 随 VS2010 一起提供,因此不需要额外的依赖项)。
T4是一个代码生成工具,可以帮助减少“繁琐”代码的维护。将 ASP/PHP 视为代码。它也类似于 XML/XSLT。
有关 T4 的精彩介绍,请参阅 Oleg Sychs 博客。
在这种情况下,代码生成的好处是,即使生成的代码是冗余的,您维护的代码(T4 模板)也不是冗余的,或者至少冗余程度较低。
因此,考虑到您提供的示例,我编写了这个 T4 模板:
(如果您想在 Visual Studio 中尝试此模板,请单击“添加新项”,选择类模板,但将扩展名从 .cs 更改为 .tt,将以下源粘贴到 .tt 文件中并保存。保存后结果应为相应的 .cs 文件)
最后会产生以下输出:
A tool you could consider is T4 (T4 is shipped with VS2010 so no additional dependencies needed).
T4 is a code-generation tool which can help reduce the maintainance of "tedious" code. Think ASP/PHP for code. It's also similar to XML/XSLT.
For an excellent introduction to T4 please see Oleg Sychs blog.
The benefits of code-generation in a case like this is that even though the generated code is redundant the code that you maintain (the T4 template) isn't or at least is less redundant.
So thinking about the sample you provided I wrote this T4 template:
(If you would like to try this template in Visual Studio click Add New Item, chose class template but change the extension from .cs to .tt, paste the following source in the .tt file and save. After save the result should be in the corresponding .cs file)
Finally this produces this output: