【发布时间】:2011-07-25 19:46:12
【问题描述】:
我有一个返回ILookup 的方法。在某些情况下,我想返回一个空的ILookup 作为提前退出。构造一个空的ILookup 的最佳方法是什么?
【问题讨论】:
-
您在 ILookup
中使用哪些类型作为 K 和 T?
我有一个返回ILookup 的方法。在某些情况下,我想返回一个空的ILookup 作为提前退出。构造一个空的ILookup 的最佳方法是什么?
【问题讨论】:
除了mquander 和Vasile Bujac 的答案之外,您可以创建一个漂亮、直接的单例EmptyLookup<K,E> 类,如下所示。 (在我看来,按照 Vasile 的回答创建完整的 ILookup<K,E> 实现似乎没有多大好处。)
var empty = EmptyLookup<int, string>.Instance;
// ...
public static class EmptyLookup<TKey, TElement>
{
private static readonly ILookup<TKey, TElement> _instance
= Enumerable.Empty<TElement>().ToLookup(x => default(TKey));
public static ILookup<TKey, TElement> Instance
{
get { return _instance; }
}
}
【讨论】:
没有内置的,所以我只需要编写一个扩展方法来运行类似于new T[0].ToLookup<K, T>(x => default(K)); 的东西
我强烈怀疑在这里返回 null 会更正确。几乎从不希望从返回集合(而不是空集合)的方法中返回 null。我完全不同意那些建议的人。
【讨论】:
null。
您可以为空查找创建一个单例类。
using System.Linq;
public sealed class EmptyLookup<T, K> : ILookup<T, K>
{
public static readonly EmptyLookup<T, K> Instance { get; }
= new EmptyLookup<T, K>();
private EmptyLookup() { }
public bool Contains(T key) => false;
public int Count => 0;
public IEnumerable<K> this[T key] => Enumerable.Empty<K>();
public IEnumerator<IGrouping<T, K>> GetEnumerator()
=> Enumerable.Empty<IGrouping<K, V>>().GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
}
那么你可以这样写代码:
var x = EmptyLookup<int, int>.Instance;
创建新类的好处是可以使用“is”运算符并检查类型是否相等:
if (x is EmptyLookup<,>) {
// ....
}
【讨论】:
Lookup<K, V>的.NET实现如果在集合中找不到key,则返回一个空序列
基于LukeH 的答案,我将使用Empty<TKey, TElement> 方法创建一个静态类Lookup。这种方式你可以使用的方式和Enumerable.Empty<T>一样。
public static class Lookup
{
public static ILookup<TKey, TElement> Empty<TKey, TElement>()
=> Enumerable.Empty<TElement>().ToLookup(x => default(TKey));
}
示例用法:Lookup.Empty<string, string>()
【讨论】:
Enumerable.Empty<T> 相同的方式缓存此方法的结果。如referencesource.microsoft.com/#System.Core/System/Linq/… 所示,它正在调用内部类中的静态属性。
创建一个空列表,然后对其执行 ToLookup(),如下所示:
List<Point> items = new List<Point>();
ILookup<int, int> lookup = items.ToLookup(p => p.X, p => p.Y);
祝你好运!
【讨论】:
感谢@mqp 的好主意。我可以基于该方法提出几种扩展方法:
public static class IEnumerableExtensions
{
public static ILookup<TKey, TElement> ToEmptyLookup<TKey, TElement>(this IEnumerable<TElement> elements) => new TElement[0].ToLookup(k => default(TKey));
public static ILookup<TKey, TElement> ToEmptyLookup<TKey, TElement>(this IDictionary<TKey, TElement> elements) => new TElement[0].ToLookup(k => default(TKey));
public static ILookup<TKey, TElement> ToEmptyLookup<TKey, TElement>(this IGrouping<TKey, TElement> elements) => new TElement[0].ToLookup(k => default(TKey));
public static ILookup<TKey, TElement> ToEmptyLookup<TKey, TElement>(this IEnumerable<ILookup<TKey, TElement>> elements) => new TElement[0].ToLookup(k => default(TKey));
}
【讨论】:
或者更符合 LINQ 精神的东西:
public static class Utility
{
public static ILookup<TKey, TElement> EmptyLookup<TKey, TElement>(Func<TKey, TKey> keySelector,
Func<TKey, TElement> elementSelector)
{
return Enumerable.Empty<TKey>().ToLookup(keySelector, elementSelector);
}
}
【讨论】:
你可以返回 null
或异常
但你应该在课堂评论中注明
补充:+这比一些扩展方法更明显
【讨论】:
Ks 到Ts,并且碰巧没有Ks 或Ts,那么很明显该函数应该返回一个空查找。返回 null 或抛出异常是没有任何意义的,并且突然不得不到处检查它,如果你返回一个空的查找,它就会开箱即用。