40

有 3 种方法可以将项目添加到大多数列表...

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

而且您通常希望它们的行为或多或少相同。但是,LINQEntitySet<T>是……在 3.5 和 4.0 上都是特殊的;IListAPI不会将该集合标记为“已分配”——其他两种机制会这样做——这听起来很简单,但重要的是它严重影响了样板代码中的序列化(即导致它被跳过)。

例子:

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,3.5 中也存在问题set.Assign(set),但现在已在 4.0 中修复。)

4

3 回答 3

20

有趣的是,现在已经为多个版本确定了这一点(您说 3.5 的问题已在 4.0 中修复)。这是 2007 年的一篇文章。4.0中的其余IList方法正确地与IList<T>方法相关联。我认为有 2 种可能的解释(错误/功能种类):

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

可能两者兼而有之 - 框架内的其他代码所依赖的错误。好像有人对自己说:

没有人真的会把它转换成 IList 然后调用 Add 方法,对吧?

于 2011-06-01T00:10:30.353 回答
8

令人惊讶的是,差异似乎源于IList.AddandIList<T>.Add方法实际上具有不同的语义

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

这种差异的明显原因是IList.Add接口方法被定义为返回添加实体的索引,对于一个典型的实现来说,它IList.Add总是CountAdd.

无论如何,由于这两种实现是故意不同的,因此作者似乎只是不小心忽略了版本中的this.OnModified()调用。IList.Add

于 2011-06-01T00:16:14.470 回答
4

对我来说似乎是一个错误。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方法超出了我的范围。

于 2011-06-01T00:02:20.193 回答