40

ほとんどのリストにアイテムを追加する方法は3つあります...

  • 通常、直接パブリックAPIメソッドを介してAdd(SomeType)
  • IList<T>.Add(T)汎用インターフェース経由
  • 非ジェネリックIList.Add(object)インターフェイス方式を介して

そしてあなたは通常それらが多かれ少なかれ同じように振る舞うことを期待します。ただし、LINQEntitySet<T>は...3.5と4.0の両方に固有です。IListAPIは、セットに「割り当て済み」のフラグを付けません(他の2つのメカニズムフラグを立てます)。これは些細なことのように聞こえますが、ボイラープレートコードのシリアル化に大きく影響する(つまり、スキップされる)という点で重要です。

例:

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. これは、Microsoftがまだ修正していない実際のバグです。
  2. これは、他のMicrosoftコードが、を設定せずにアイテムを追加するために利用しいる機能です。HasLoadedOrAssignedValues

それはおそらく両方です-フレームワーク内の他のコードが期待しているバグです。誰かが自分自身に言ったように聞こえます:

これを実際にIListにキャストしてから、Addメソッドを呼び出す人は誰もいないでしょう。

于 2011-06-01T00:10:30.353 に答える
8

IList.Add驚いたことに、違いは、メソッドとIList<T>.Addメソッドが実際には異なるセマンティクスを持っているという事実に根ざしているようです。

  • IList.Add追加するエンティティがすでに存在する場合、メソッドは失敗します
  • エンティティがすでに存在する場合、このLIst<T>.Addメソッドはエンティティを削除してから再度追加します

この違いの明らかな理由IList.Addは、追加されたエンティティのインデックスを返すようにインターフェイスメソッドが定義されていることです。これは、の一般的な実装でIList.Addは、常にCount前のコレクションのインデックスになりAddます。

いずれにせよ、2つの実装は意図的に異なるため、作成者はバージョンのthis.OnModified()呼び出しを誤って省略したようです。IList.Add

于 2011-06-01T00:16:14.470 に答える
4

私にはバグのように見えます。ILSpyは、2つの実装の違いを示しています。

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 に答える