问题 是否可以在KeyedCollection中使用int作为键


通常我需要一组带有数字标识符的非顺序对象。我喜欢使用KeyedCollection,但我认为这有一个严重的缺点。如果对密钥使用int,则不能再通过索引访问集合的成员(集合[index]现在真的是集合[key])。这是一个严重的问题,以避免使用int作为键?什么是更好的选择? (也许是int.ToString()?)

我之前没有遇到任何重大问题,但最近我遇到了一个令人讨厌的障碍,其中针对KeyedCollection的XML序列化  如果键是int,则工作 .NET中的一个错误


9943
2017-10-14 16:49


起源



答案:


基本上你需要决定这个类的用户是否可能因为他们不能这样做而感到困惑,例如:

for(int i=0; i=< myCollection.Count; i++)
{
    ... myCollection[i] ...
}

虽然他们当然可以使用foreach,或使用演员:

for(int i=0; i=< myCollection.Count; i++)
{
    ... ((Collection<MyType>)myCollection)[i] ...
}

这不是一个简单的决定,因为它很容易导致heisenbugs。我决定在我的一个应用程序中允许它,其中该类用户的访问几乎完全是按键。

我不确定我是否会为共享类库执行此操作:通常我会避免在公共API中公开KeyedCollection:相反,我会在公共API中公开IList <T>,以及API的消费者需要键控访问可以使用构造函数定义自己的内部KeyedCollection,该构造函数接受IEnumerable <TItem>并使用它填充集合。这意味着您可以从API检索的列表中轻松构建新的KeyedCollection。

关于序列化,还存在性能问题 我向Microsoft Connect报告过:KeyedCollection维护一个内部字典和一个列表,并对两者进行序列化 - 序列化列表就足够了,因为字典可以在反序列化时轻松地重新创建。

出于这个原因以及XmlSerialization错误,我建议您避免序列化KeyedCollection - 而只是序列化KeyedCollection.Items列表。

我不喜欢 将int键包装在另一种类型中的建议。简单地添加复杂性似乎是错误的,因此类型可以用作KeyedCollection中的项目。我使用字符串键(ToString)而不是这样做 - 这就像VB6 Collection类。

FWIW,我问道 同样的问题 前段时间在MSDN论坛上。 FxCop团队成员的回复,但没有确凿的指导方针。


7
2017-10-14 17:18



我认为我同意不公开一个KeyedCollection,它在API中使用int作为键。这可能会导致另一端的错误。序列化问题肯定是不能序列化集合本身的一个很好的理由,而只是项目(这是我在我的案例中所做的)。 - Jon B
还有一种方法是使用从KeyedCollection派生的类专门用于整数键,其中包括GetByIndex或ItemAt方法,该方法使用内部Items集合来获取适当的项。这使您可以利用现有的KeyedCollection,而不是下面涉及扩充Collection的方法。 - Thomas S. Trias
您可以通过threshold = -1禁用字典 - paparazzo


答案:


基本上你需要决定这个类的用户是否可能因为他们不能这样做而感到困惑,例如:

for(int i=0; i=< myCollection.Count; i++)
{
    ... myCollection[i] ...
}

虽然他们当然可以使用foreach,或使用演员:

for(int i=0; i=< myCollection.Count; i++)
{
    ... ((Collection<MyType>)myCollection)[i] ...
}

这不是一个简单的决定,因为它很容易导致heisenbugs。我决定在我的一个应用程序中允许它,其中该类用户的访问几乎完全是按键。

我不确定我是否会为共享类库执行此操作:通常我会避免在公共API中公开KeyedCollection:相反,我会在公共API中公开IList <T>,以及API的消费者需要键控访问可以使用构造函数定义自己的内部KeyedCollection,该构造函数接受IEnumerable <TItem>并使用它填充集合。这意味着您可以从API检索的列表中轻松构建新的KeyedCollection。

关于序列化,还存在性能问题 我向Microsoft Connect报告过:KeyedCollection维护一个内部字典和一个列表,并对两者进行序列化 - 序列化列表就足够了,因为字典可以在反序列化时轻松地重新创建。

出于这个原因以及XmlSerialization错误,我建议您避免序列化KeyedCollection - 而只是序列化KeyedCollection.Items列表。

我不喜欢 将int键包装在另一种类型中的建议。简单地添加复杂性似乎是错误的,因此类型可以用作KeyedCollection中的项目。我使用字符串键(ToString)而不是这样做 - 这就像VB6 Collection类。

FWIW,我问道 同样的问题 前段时间在MSDN论坛上。 FxCop团队成员的回复,但没有确凿的指导方针。


7
2017-10-14 17:18



我认为我同意不公开一个KeyedCollection,它在API中使用int作为键。这可能会导致另一端的错误。序列化问题肯定是不能序列化集合本身的一个很好的理由,而只是项目(这是我在我的案例中所做的)。 - Jon B
还有一种方法是使用从KeyedCollection派生的类专门用于整数键,其中包括GetByIndex或ItemAt方法,该方法使用内部Items集合来获取适当的项。这使您可以利用现有的KeyedCollection,而不是下面涉及扩充Collection的方法。 - Thomas S. Trias
您可以通过threshold = -1禁用字典 - paparazzo


一个简单的解决方案可能是包装 int 进入另一种类型,为重载决策创建一个独特的类型。如果你使用 struct,这个包装器没有任何额外的开销:

struct Id {
    public int Value;

    public Id(int value) { Value = value; }

    override int GetHashCode() { return Value.GetHashCode(); }

    // … Equals method.
}

3
2017-10-14 16:58



这是一个好主意。但是,使结构不可变也总是一个好主意。这避免了如下的混淆:Id id; id.Value = 2; Id id2 = id; id2.Value = 4; //之后是什么id.Value?很多人都希望它是4.(抱歉格式化;如果可以,我会使用换行符) - Neil


最好添加一个 GetById(int) 集合类型的方法。 Collection<T> 如果您不需要任何其他键来访问包含的对象,则可以使用它:

public class FooCollection : Collection<Foo>
 { Dictionary<int,Foo> dict = new Dictionary<int,Foo>();

   public Foo GetById(int id) { return dict[id]; }

   public bool Contains(int id) { return  dict.Containskey(id);}

   protected override void InsertItem(Foo f)
    { dict[f.Id] = f;
      base.InsertItem(f);
    }

   protected override void ClearItems()
    { dict.Clear();
      base.ClearItems();
    }

   protected override void RemoveItem(int index)
    { dict.Remove(base.Items[index].Id);
      base.RemoveItem(index);
    }

   protected override void SetItem(int index, Foo item)
    { dict.Remove(base.Items[index].Id);
      dict[item.Id] = item;
      base.SetItem(index, item);
    }
 }









 }

2
2017-10-14 16:52



这是最好的答案imo - nawfal


KeyedCollection中的键应该是唯一的,并且可以从收集的对象中快速导出。例如,给定一个person类,它可以是SSN属性,或者甚至可以连接FirstName和LastName属性(如果结果已知是唯一的)。如果ID合法地是正在收集的对象的字段,则它是该密钥的有效候选者。但也许尝试将其转换为字符串,以避免碰撞。


1
2017-10-14 16:54