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变化):

  1. 这是微软尚未解决的一个实际错误。
  2. 这是一些其他Microsoft代码正在利用利用添加项目而不设置HasLoadedOrAssignedValues

这可能是两个 – 框架内的其他代码所依赖的错误。 听起来像有人自言自语:

没有人真的将这个投入到IList中,然后调用Add方法,对吧?

令人惊讶的是,这种差异似乎根植于IList.AddIList<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可能依赖的几个事件调用者( OnListChangedOnModified )来跟踪其更改。 如果这是故意的,我也会期待他们也放弃OnAdd的呼叫。

为什么他们不只是简单的把IList.AddTEntity的值,并且调用通用的Add方法已经超越了我自己。