征求意见:拦截列表/集合的更改
尽管 BindingList
和 ObservableCollection
提供了检测列表更改的机制,但它们不支持在之前检测/拦截更改的机制它们发生了。
我正在编写几个接口来支持这一点,但我想征求您的意见。
选项 1:列出每种类型操作的引发事件
在这里,消费者可能会编写如下代码:
public class Order : Entity
{
public Order()
{
this.OrderItems = new List<OrderItem>();
this.OrderItems.InsertingItem += new ListChangingEventHandler<OrderItem>(OrderItems_InsertingItem);
this.OrderItems.SettingItem += new ListChangingEventHandler<OrderItem>(OrderItems_SettingItem);
this.OrderItems.RemovingItem += new ListChangingEventHandler<OrderItem>(OrderItems_RemovingItem);
}
virtual public List<OrderItem> OrderItems { get; internal set; }
void OrderItems_InsertingItem(object sender, IOperationEventArgs<OrderItem> e)
{
if (!validationPasses)
{
e.Cancel = true;
return;
}
e.Item.Parent = this;
}
void OrderItems_SettingItem(object sender, IOperationEventArgs<OrderItem> e)
{
if (!validationPasses)
{
e.Cancel = true;
return;
}
e.Item.Parent = this;
}
void OrderItems_RemovingItem(object sender, IOperationEventArgs<OrderItem> e)
{
if (!validationPasses)
{
e.Cancel = true;
return;
}
e.Item.Parent = null;
}
}
选项 2:列出引发单个事件,并且操作由事件参数确定在
这里,消费者可能会编写如下代码:
public class Order : Entity
{
public Order()
{
this.OrderItems = new List<OrderItem>();
this.OrderItems.ListChanging += new ListChangingEventHandler<OrderItem>(OrderItems_ListChanging);
}
virtual public List<OrderItem> OrderItems { get; internal set; }
void OrderItems_ListChanging(object sender, IOperationEventArgs<OrderItem> e)
{
switch (e.Action)
{
case ListChangingType.Inserting:
case ListChangingType.Setting:
if (validationPasses)
{
e.Item.Parent = this;
}
else
{
e.Cancel = true;
}
break;
case ListChangingType.Removing:
if (validationPasses)
{
e.Item.Parent = null;
}
else
{
e.Cancel = true;
}
break;
}
}
}
背景:我正在编写一组代表 DDD 核心组件的通用接口/类,并且我正在制作 来源代码可用(因此需要创建友好的界面)。
这个问题是关于使界面尽可能具有凝聚力,以便消费者可以派生并实现自己的集合,而不会丢失核心语义。
PS:请不要建议使用 AddXYZ()< /code> 和
RemoveXYZ()
方法用于每个列表,因为我已经不考虑这个想法了。
PPS:我必须包括使用 .NET 2.0 的开发人员:)
相关问题。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我建议在适当的情况下创建与 ObservableCollection 并行的东西。具体来说,我建议遵循现有的收集变更通知技术。类似于:
这将遵循既定的模式,以便客户端已经熟悉公开的接口——其中三个接口已经存在。使用现有接口还可以与其他现有的 .NET 技术进行更适当的交互,例如 WPF(它绑定 INotifyPropertyChanged 和 INotifyCollectionChanged 接口。)
我希望
INotifyCollectionChanged
接口看起来像这样:如果您希望添加取消支持,只需将可写的
bool Cancel
属性添加到CollectionChangingEventArgs
即可,集合将读取以确定是否执行即将发生的更改。我想这属于您的选项 2。这是要走的路,因为为了与监视更改集合的其他 .net 技术正确互操作,您无论如何都必须为
INotifyCollectionChanged
实现它。这肯定会遵循界面中“最少惊喜”的政策。I would suggest creating something that parallels the
ObservableCollection<T>
where appropriate. Specifically, I would suggest following the existing techniques for notification of change of collection. Something like:This will follow established patterns so that clients are already familiar with the exposed interfaces-- three of the interfaces already exist. The use of existing interfaces will also allow more proper interaction with other already existing .NET technologies, such as WPF (which binds against the
INotifyPropertyChanged
andINotifyCollectionChanged
interfaces.)I would expect the
INotifyCollectionChanged
interface to look something like:If you wish to add cancellation support, simply add a writable
bool Cancel
property toCollectionChangingEventArgs
that the collection will read to determine whether to execute the change that's about to occur.I suppose this falls under your Option 2. This is the way to go because, to interoperate properly with other .net technologies that monitor changing collections, you're going to have to implement it anyway for
INotifyCollectionChanged
. This will definitely follow the policy of "Least Surprise" in your interface.我会推荐单独的活动。我觉得更清楚了。
编辑:
您可能需要考虑前后事件,例如插入、插入或 VB 人员拥有的 BeforeInsert、AfterInsert。这将为用户提供更大的灵活性。
I would recomend seperate events. It seems more clear to me.
EDIT:
You might want to cosider a before and after event such as Inserting,Inserted or as the VB guys have it BeforeInsert, AfterInsert. This will give the user more flexability.
看看这个链接,也许这就是您正在寻找的,基于通用列表的对象,它充当列表,但具有内置事件,例如 BeforeItemAdded、ItemAdded、BeforeItemRemoved、ItemRemoved 和 ItemsCleared。
希望这有帮助,汤姆。 :)
Have a look at this link, maybe that is what you are looking for, a Generic List based object that acts as a List but with built-in events such as BeforeItemAdded, ItemAdded, BeforeItemRemoved, ItemRemoved and ItemsCleared.
Hope this helps, Tom. :)
事实上,您会惊讶地发现创建这样的集合是多么容易。
看一下
System.Collections.ObjectModel.Collection
。这是一个旨在用于此类事情的类。它有一些虚拟方法(每个操作一个),您可以很好地覆盖和控制它们。我推荐选项 1,因为它更清晰、更直接。
下面是一个可用于此类目的的示例:
您还可以修改 ListChangeEventArgs 以使其具有名称为“Cancel”的 bool 属性,并控制是否在集合中进行更改。
如果您需要此类功能,则后续事件也可能很有用。
当然,您不必使用每个集合的所有事件,或者如果确实有必要,可能还有其他方法来解决问题,具体取决于您为什么需要此功能。
编辑:
如果您真的只想验证项目并将其 Parent 属性设置为实体实例,您实际上可以编写一个完全执行此操作的集合,或者以另一种方式概括问题的方法。您可以向它传递一个验证项目的委托,以及另一个告诉它在添加或删除项目时要执行的操作的委托。
例如,您可以使用 Action 委托来实现此目的。
您可以这样使用它:
这种方法的主要好处是您不必费心处理事件处理程序或委托,因为您需要的所有内容都可以使用 lambda 表达式编写,但是如果您需要更高级的东西,您可以始终使用真正的委托而不是它们。
这是该集合的一个示例:
出于此类目的,我认为这是最干净的方法。
Actually, you will be surprised how easily you can create a collection like that.
Take a look at
System.Collections.ObjectModel.Collection<T>
. That is a class which is intended to be used for such things. It has a few virtual methods (one for every operation) which you can override and control very well.I would recommend Option 1, since it is more clear and straightforward.
Here is an example which you can use for such purposes:
You could also modify the ListChangeEventArgs to have a bool property with the name "Cancel", and control wheter to do the change or not in the collection.
The after events could also be useful, if you need such functionality.
Of course, you won't have to use all events of every collections, or if it is really necessary, there may be other ways to solve the problem depending on why do you need this functionality.
EDIT:
If you really only want to validate the items and set their Parent property to an entity instance, you can actually write a collection which does exactly that, or something that generalizes the problem in another way. You could pass it a delegate which validates the item, and and another which tells it what to do when an item is added or removed.
For example, you can achieve this using the Action delegate.
You could consume it this way:
The major benefit of this approach is that you don't have to bother with event handlers or delegates, beacuse all that you need can be written using lambda expressions, however if you need something more advanced, you can always use a real delegate instead of them.
This is an example of the collection:
For such purposes, I think this is the cleanest way to go.