Şaf*_*Gür 35 .net c# collections interface .net-4.5
如果我有一个需要参数的方法,
Count
房产这个参数的类型应该是什么?我会IList<T>
在.NET 4.5之前选择,因为没有其他可索引的集合接口,并且数组实现它,这是一个很大的优点.
但.NET 4.5引入了新的IReadOnlyList<T>
界面,我也希望我的方法能够支持它.我如何编写这种方法来支持这两种方法,IList<T>
而IReadOnlyList<T>
不是违反像DRY这样的基本原则?
编辑:丹尼尔的回答给了我一些想法:
public void Foo<T>(IList<T> list)
=> Foo(list, list.Count, (c, i) => c[i]);
public void Foo<T>(IReadOnlyList<T> list)
=> Foo(list, list.Count, (c, i) => c[i]);
private void Foo<TList, TItem>(
TList list, int count, Func<TList, int, TItem> indexer)
where TList : IEnumerable<TItem>
{
// Stuff
}
Run Code Online (Sandbox Code Playgroud)
编辑2:或者我可以接受一个IReadOnlyList<T>
并提供这样的帮助:
public static class CollectionEx
{
public static IReadOnlyList<T> AsReadOnly<T>(this IList<T> list)
{
if (list == null)
throw new ArgumentNullException(nameof(list));
return list as IReadOnlyList<T> ?? new ReadOnlyWrapper<T>(list);
}
private sealed class ReadOnlyWrapper<T> : IReadOnlyList<T>
{
private readonly IList<T> _list;
public ReadOnlyWrapper(IList<T> list) => _list = list;
public int Count => _list.Count;
public T this[int index] => _list[index];
public IEnumerator<T> GetEnumerator() => _list.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
Run Code Online (Sandbox Code Playgroud)
然后我可以称之为 Foo(list.AsReadOnly())
编辑3: 数组实现两者,类IList<T>
也是IReadOnlyList<T>
如此List<T>
.这使得很难找到一个实现IList<T>
但不实现的类IReadOnlyList<T>
.
Dan*_*rth 23
你在这里运气不好.IList<T>
没有实现IReadOnlyList<T>
.List<T>
确实实现了两个接口,但我认为这不是你想要的.
但是,您可以使用LINQ:
Count()
扩展方法内部检查其实实例是否是一个集合,然后使用Count
属性.ElementAt()
扩展方法内部检查其实实例是否是列表,比使用索引.由于IList<T>
并且IReadOnlyList<T>
不共享任何有用的“祖先”,并且如果您不希望您的方法接受任何其他类型的参数,那么您唯一可以做的就是提供两个重载。
如果您决定重用代码是最优先考虑的,那么您可以让这些重载将调用转发到一个private
方法,IEnumerable<T>
该方法以 Daniel 建议的方式接受和使用 LINQ,实际上是让 LINQ 在运行时进行规范化。
但是恕我直言,最好只复制/粘贴一次代码并保留两个仅在参数类型上不同的独立重载;我不相信这种规模的微架构提供任何有形的东西,另一方面,它需要不明显的操作并且速度较慢。
如果您更关心维护DRY的主要性能而不是性能,则可以使用dynamic
,如下所示:
public void Do<T>(IList<T> collection)
{
DoInternal(collection, collection.Count, i => collection[i]);
}
public void Do<T>(IReadOnlyList<T> collection)
{
DoInternal(collection, collection.Count, i => collection[i]);
}
private void DoInternal(dynamic collection, int count, Func<int, T> indexer)
{
// Get the count.
int count = collection.Count;
}
Run Code Online (Sandbox Code Playgroud)
但是,我不能真诚地说我会推荐这个,因为陷阱太大了:
collection
inDoInternal
都将在运行时解决。你失去了类型安全、编译时检查等。你的助手建议是最有用的,但我认为你应该把它翻过来;鉴于该IReadOnlyList<T>
接口是在 .NET 4.5 中引入的,许多 API 不支持它,但支持该IList<T>
接口。
也就是说,您应该创建一个AsList
包装器,它IReadOnlyList<T>
在IList<T>
实现中接受并返回一个包装器。
但是,如果您想强调您正在使用的 API IReadOnlyList<T>
(强调您没有改变数据的事实),那么AsReadOnlyList
您现在拥有的扩展会更合适,但我会进行以下优化到AsReadOnly
:
public static IReadOnlyList<T> AsReadOnly<T>(this IList<T> collection)
{
if (collection == null)
throw new ArgumentNullException("collection");
// Type-sniff, no need to create a wrapper when collection
// is an IReadOnlyList<T> *already*.
IReadOnlyList<T> list = collection as IReadOnlyList<T>;
// If not null, return that.
if (list != null) return list;
// Wrap.
return new ReadOnlyWrapper<T>(collection);
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
32125 次 |
最近记录: |