如何把ICollection<T>变成IReadOnlyCollection<T>?
How to turn ICollection<T> into IReadOnlyCollection<T>?
当我在 C# 中有一个 ICollection<T>
变量时,我无法将它传递给需要 IReadOnlyCollection<T>
:
的函数
public void Foo()
{
ICollection<int> data = new List<int>();
// Bar(data); // Not allowed: Cannot implicitly cast ICollection<int> to IReadOnlyCollection<int>
Bar(data.ToList()); // Works, since List<T> implements IReadOnlyCollection<T>
}
public void Bar(IReadOnlyCollection<int> data)
{
if (data.Count == 1) { /* ... */ }
// ...
}
显然问题是 ICollection<T>
没有继承自 IReadOnlyCollection<T>
- 但为什么呢? ICollection<T>
应该是 IReadOnlyCollection<T>
的完整功能集加上修改集合的功能。
传递参数的最佳解决方案是什么?
一方面,由于我不想更改 Bar
中的集合,而只需要对集合进行计数和迭代,因此我想要一个 IReadOnlyCollection
。
另一方面,我不想每次调用该函数时都创建一个新的列表对象。
没有标准的解决方案 AFAIK,但是像这样制作自己的解决方案并不难
public static class MyExtensions
{
public static IReadOnlyCollection<T> AsReadOnly<T>(this ICollection<T> source)
{
if (source == null) throw new ArgumentNullException("source");
return source as IReadOnlyCollection<T> ?? new ReadOnlyCollectionAdapter<T>(source);
}
sealed class ReadOnlyCollectionAdapter<T> : IReadOnlyCollection<T>
{
readonly ICollection<T> source;
public ReadOnlyCollectionAdapter(ICollection<T> source) => this.source = source;
public int Count => source.Count;
public IEnumerator<T> GetEnumerator() => source.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
然后如下使用
Bar(data.AsReadOnly());
您可以在实现 IReadOnlyCollection<T>
的同时创建一个 class 来组成 ICollection<T>
。您还可以创建一个扩展方法来进行包装(从而允许泛型类型推断):
public class ReadOnlyCollectionWrapper<T> : IReadOnlyCollection<T>
{
private ICollection<T> collection;
public ReadOnlyCollectionWrapper(ICollection<T> collection)
{
this.collection = collection;
}
public int Count
{
get { return collection.Count; }
}
public IEnumerator<T> GetEnumerator()
{
return collection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return collection.GetEnumerator();
}
}
public static class ReadOnlyCollectionWrapper
{
public static IReadOnlyCollection<T> AsReadOnly<T>(this ICollection<T> collection)
{
return new ReadOnlyCollectionWrapper<T>(collection);
}
}
当我在 C# 中有一个 ICollection<T>
变量时,我无法将它传递给需要 IReadOnlyCollection<T>
:
public void Foo()
{
ICollection<int> data = new List<int>();
// Bar(data); // Not allowed: Cannot implicitly cast ICollection<int> to IReadOnlyCollection<int>
Bar(data.ToList()); // Works, since List<T> implements IReadOnlyCollection<T>
}
public void Bar(IReadOnlyCollection<int> data)
{
if (data.Count == 1) { /* ... */ }
// ...
}
显然问题是 ICollection<T>
没有继承自 IReadOnlyCollection<T>
- 但为什么呢? ICollection<T>
应该是 IReadOnlyCollection<T>
的完整功能集加上修改集合的功能。
传递参数的最佳解决方案是什么?
一方面,由于我不想更改 Bar
中的集合,而只需要对集合进行计数和迭代,因此我想要一个 IReadOnlyCollection
。
另一方面,我不想每次调用该函数时都创建一个新的列表对象。
没有标准的解决方案 AFAIK,但是像这样制作自己的解决方案并不难
public static class MyExtensions
{
public static IReadOnlyCollection<T> AsReadOnly<T>(this ICollection<T> source)
{
if (source == null) throw new ArgumentNullException("source");
return source as IReadOnlyCollection<T> ?? new ReadOnlyCollectionAdapter<T>(source);
}
sealed class ReadOnlyCollectionAdapter<T> : IReadOnlyCollection<T>
{
readonly ICollection<T> source;
public ReadOnlyCollectionAdapter(ICollection<T> source) => this.source = source;
public int Count => source.Count;
public IEnumerator<T> GetEnumerator() => source.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
然后如下使用
Bar(data.AsReadOnly());
您可以在实现 IReadOnlyCollection<T>
的同时创建一个 class 来组成 ICollection<T>
。您还可以创建一个扩展方法来进行包装(从而允许泛型类型推断):
public class ReadOnlyCollectionWrapper<T> : IReadOnlyCollection<T>
{
private ICollection<T> collection;
public ReadOnlyCollectionWrapper(ICollection<T> collection)
{
this.collection = collection;
}
public int Count
{
get { return collection.Count; }
}
public IEnumerator<T> GetEnumerator()
{
return collection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return collection.GetEnumerator();
}
}
public static class ReadOnlyCollectionWrapper
{
public static IReadOnlyCollection<T> AsReadOnly<T>(this ICollection<T> collection)
{
return new ReadOnlyCollectionWrapper<T>(collection);
}
}