是有可能得到一个IEnumerator 从T []?
-
21-09-2019 - |
题
比方说,我想创建一个集合类是线程安全的默认。
在内部,类有称为List<T>
受保护Values
属性。
对于初学者来说,是有意义的具有类实现ICollection<T>
。一些该接口的成员是非常容易实现;例如,Count
返回this.Values.Count
。
但是实现ICollection<T>
要求我执行IEnumerable<T>
以及IEnumerable
(非通用),这是一个有点棘手的一个线程安全的集合。
当然,我总是可以扔在NotSupportedException
和IEnumerable<T>.GetEnumerator
一个IEnumerable.GetEnumerator
,但感觉就像一种逃避我。
我已经有其上getValues
并返回锁定一个拷贝在一个Values
阵列形式的线程安全T[]
功能。所以我的想法是通过返回GetEnumerator
实现this.getValues().GetEnumerator()
使下面的代码实际上是线程安全的:
ThreadSafeCollection coll = new ThreadSafeCollection ();
// add some items to coll
foreach (T value in coll) {
// do something with value
}
不幸的是该实施方式中只似乎为IEnumerable.GetEnumerator
,而不是通用版本工作(并且因此上述的代码引发InvalidCastException
)。
我有一个想法,这似乎工作,是它调用之前T[]
从getValues
铸IEnumerable<T>
返回值的GetEnumerator
。另一种方法是,以改变getValues
在首位返回IEnumerable<T>
,但随后的非通用IEnumerable.GetEnumerator
简单地将返回值从getValues
到非通用IEnumerable
。但我真的不能决定,如果这些方法觉得草率或完全可以接受的。
在任何情况下,任何人都不会有如何去这样做一个更好的主意吗?我听到的.Synchronized
方法,但他们似乎只可在System.Collections
命名空间非泛型集合。也许有这个已经存在于.NET泛型的变种,我根本不知道?
解决方案
大多数集合指定你不能同时迭代集合添加或删除的东西。由此可见,一个线程安全的集合,要从修改集合,而任何线程迭代锁定其他线程。这应该是很容易与迭代器的语法做了,不需要你做一个副本:
public IEnumerator<T> GetEnumerator()
{
lock (this.Values) // or an internal mutex you use for synchronization
{
foreach (T val in this.Values)
{
yield return val;
}
}
yield break;
}
这假定所有可能还会修改集合的其他操作锁定this.Values
。只要这是真的,你应该罚款。
其他提示
不幸的是该实施方式中只似乎为IEnumerable.GetEnumerator,而不是通用版本工作(因此上面的代码引发InvalidCastException)。
似乎很奇怪我。你有没有实现的非通用IEnumerable的明确?即你写过
public IEnumerator<T> GetEnumerator() { ...}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator<T>(); }
此外,你有没有试着用迭代器的语法来实现IEnumerable。应该很容易:
public IEnumerator<T> GetEnumerator()
{
T[] values;
lock(this.Values)
values = this.Values.ToArray();
foreach(var value in values)
yield return value;
}
如果您已经试过,为什么没有适合您的需求?
在型T[]
具有特殊realtionship到它导出类型System.Array
。之前在.NET(否则语法可能是T[]
)引入泛型像Array<T>
阵列被做了。
在型System.Array
具有一个 GetEnumerator()
实例方法,public
。此方法返回非通用IEnumerator
(因为.NET 1)。而System.Array
有GetEnumerator()
没有明确的接口实现。这说明你的看法。
但是,当在泛型.NET 2.0进行了介绍,一个特殊的“黑客”被制成具有T[]
实施IList<T>
及其基接口(其中IEnumerable<T>
是其中之一)。为此,我认为这是完全合理的使用:
((IList<T>)(this.getValues())).GetEnumerator()
在.NET其中我检查此的版本,以下类存在:
namespace System
{
public abstract class Array
{
private sealed class SZArrayEnumerator // instance of this is returned with standard GetEnumerator() on a T[]
{
}
}
internal sealed class SZArrayHelper
{
private sealed class SZGenericArrayEnumerator<T> // instance of this is returned with generic GetEnumerator() on a T[] which has been cast to IList<T>
{
}
}
}