题 EntitySet - 是否有一个理所当然的IList.Add没有设置分配?


向大多数列表添加项目有3种方法...

  • 通常通过直接的公共API方法 Add(SomeType)
  • 通过通用 IList<T>.Add(T) 接口
  • 通过非泛型 IList.Add(object) 界面方法

你通常希望他们表现出来 或多或少 一样。但是,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) 在3.5中,但现在已在4.0中修复。)


38
2018-05-31 23:18


起源


Add()方法调用OnModified,而IList.Add()显式实现则不会。当然没有人使用它,未使用的代码有bug。 IList.Add()的返回类型肯定是bug生成器。 - Hans Passant
登录连接: connect.microsoft.com/VisualStudio/feedback/details/671989/... - Marc Gravell♦


答案:


有趣的是,现在已经确定了几个版本(你说在3.5版本中修复了3.5版本)。 这是2007年的帖子。 剩下的 IList 4.0中的方法正确绑定到 IList<T> 方法。我认为有两种可能的解释(错误/特征种类):

  1. 这是Microsoft尚未修复的实际错误。
  2. 这是其他一些Microsoft代码的功能 利用 利用添加项目  设置 HasLoadedOrAssignedValues

它可能是两者 - 框架内的其他代码所依赖的错误。听起来有人对自己说:

没有人真正将它转换为IList然后调用Add方法,对吗?


20
2018-06-01 00:10





令人惊讶的是,差异似乎植根于这样的事实 IList.Add 和 IList<T>.Add 方法实际上有 不同的语义

  • IList.Add 如果要添加的实体已存在,则方法失败
  • LIst<T>.Add 如果已经存在,则方法移除然后重新添加实体

这种差异的明显原因是 IList.Add 接口方法被定义为返回添加实体的索引,这对于典型的实现来说 IList.Add 将永远是 Count 之前的集合 Add

无论如何,因为这两个实现是故意不同的,所以作者似乎只是偶然地省略了 this.OnModified() 打电话给 IList.Add 版。


8
2018-06-01 00:16





对我来说看起来像个错误。 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实现只是忽略了调用几个事件调用者(OnListChanged 和 OnModified)LINQ to SQL可能依赖于跟踪其更改。如果这是故意的话,我本以为他们也会不再打电话给 OnAdd

他们为什么不简单地拥有 IList.Add 将值转换为 TEntity 并调用泛型 Add方法超出我的范围。


4
2018-06-01 00:02



“他们为什么不简单地拥有 IList.Add 将值转换为 TEntity 并调用泛型 Add 方法超出了我的范围。“这就是Mono的实现方式。 - LukeH