EntitySet – 有一个理智的原因,IList.Add不设置分配?
有3种方式将项目添加到大多数列表中…
- 通过直接的公共API方法,通常
Add(SomeType)
- 通过通用的
IList<T>.Add(T)
接口 - 通过非通用的
IList.Add(object)
接口方法
而你通常期望他们的performance差不多一样。 但是,LINQ的EntitySet<T>
是… 3.5和4.0的特有; IList
API 不会将这个集合标记为“分配” – 其他两种机制 – 这听起来微不足道,但重要的是它严重影响了样板代码中的序列化(即导致它被跳过)。
例:
EntitySet<string> set1 = new EntitySet<string>(); set1.Add("abc"); Debug.Assert(set1.Count == 1); // pass Debug.Assert(set1.HasLoadedOrAssignedValues, "direct"); // pass EntitySet<string> set2 = new EntitySet<string>(); IList<string> typedList = set2; typedList.Add("abc"); Debug.Assert(set2.Count == 1); // pass Debug.Assert(set2.HasLoadedOrAssignedValues, "typed list"); // pass EntitySet<string> set3 = new EntitySet<string>(); IList untypedList = set3; untypedList.Add("abc"); Debug.Assert(set3.Count == 1); // pass Debug.Assert(set3.HasLoadedOrAssignedValues, "untyped list"); // FAIL
现在…这对我来说是非常惊人的。 以至于花了我2个多小时的时间,通过代码来追踪发生的事情。 所以…
这有什么理智的原因吗? 或者这只是一个错误?
(FWIW, set.Assign(set)
也有一个问题, set.Assign(set)
在3.5,但现在固定在4.0。)
有趣的是,现在已经确定了几个版本(你曾经说过3.5版本在4.0版本中是固定的)。 这是2007年以后的一篇文章。其余的IList
4.0中的方法与IList<T>
方法正确绑定。 我认为有2个可能的解释(错误/function变化):
- 这是微软尚未解决的一个实际错误。
- 这是一些其他Microsoft代码正在
利用利用添加项目而不设置HasLoadedOrAssignedValues
。
这可能是两个 – 框架内的其他代码所依赖的错误。 听起来像有人自言自语:
没有人真的将这个投入到IList中,然后调用Add方法,对吧?
令人惊讶的是,这种差异似乎根植于IList.Add
和IList<T>.Add
方法实际上具有不同的语义 :
- 如果添加的实体已经存在,则
IList.Add
方法失败 - 如果is已经存在,则
LIst<T>.Add
方法将删除并重新添加一个实体
这种区别的明显原因是IList.Add
接口方法被定义为返回添加实体的索引,对于IList.Add
的典型实现, IList.Add
将始终是Add
之前的集合的Count
。
在任何情况下,因为这两个实现有意不同,所以似乎作者只是不小心在IList.Add
版本中省略了this.OnModified()
调用。
看起来像是一个bug。 ILSpy显示了两种实现之间的区别:
int IList.Add(object value) { TEntity tEntity = value as TEntity; if (tEntity == null || this.IndexOf(tEntity) >= 0) { throw Error.ArgumentOutOfRange("value"); } this.CheckModify(); int count = this.entities.Count; this.entities.Add(tEntity); this.OnAdd(tEntity); return count; } // System.Data.Linq.EntitySet<TEntity> /// <summary>Adds an entity.</summary> /// <param name="entity">The entity to add.</param> public void Add(TEntity entity) { if (entity == null) { throw Error.ArgumentNull("entity"); } if (entity != this.onAddEntity) { this.CheckModify(); if (!this.entities.Contains(entity)) { this.OnAdd(entity); if (this.HasSource) { this.removedEntities.Remove(entity); } this.entities.Add(entity); this.OnListChanged(ListChangedType.ItemAdded, this.entities.IndexOf(entity)); } this.OnModified(); } }
看起来像IList的实现简单地忽略了调用LINQ to SQL可能依赖的几个事件调用者( OnListChanged
和OnModified
)来跟踪其更改。 如果这是故意的,我也会期待他们也放弃OnAdd
的呼叫。
为什么他们不只是简单的把IList.Add
成TEntity
的值,并且调用通用的Add
方法已经超越了我自己。