将单个项目传递为IEnumerable <T>

有没有一种常见的方式来传递一个typesT的单个项目的方法,期望IEnumerable<T>参数? 语言是C#,框架版本2.0。

目前我正在使用一个辅助方法(它是.Net 2.0,所以我有一大堆类似于LINQ的投射/投影辅助方法),但这看起来很愚蠢:

 public static class IEnumerableExt { // usage: IEnumerableExt.FromSingleItem(someObject); public static IEnumerable<T> FromSingleItem<T>(T item) { yield return item; } } 

其他方式当然是创build和填充一个List<T>或一个Array并通过它而不是IEnumerable<T>

[编辑]作为扩展方法,它可能被命名为:

 public static class IEnumerableExt { // usage: someObject.SingleItemAsEnumerable(); public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item) { yield return item; } } 

我在这里错过了什么?

[编辑2]我们发现someObject.Yield() (作为@Peterbuild议在下面的评论)是这个扩展方法的最好的名字,主要是为了简洁,所以在这里,如果有人想要抓住它,

 public static class IEnumerableExt { /// <summary> /// Wraps this object instance into an IEnumerable&lt;T&gt; /// consisting of a single item. /// </summary> /// <typeparam name="T"> Type of the object. </typeparam> /// <param name="item"> The instance that will be wrapped. </param> /// <returns> An IEnumerable&lt;T&gt; consisting of a single item. </returns> public static IEnumerable<T> Yield<T>(this T item) { yield return item; } } 

你的帮手方法是最干净的方法,国际海事组织。 如果你传递一个列表或一个数组,那么不择手段的代码就可以投射它并改变内容,导致在某些情况下奇怪的行为。 你可以使用一个只读的集合,但这可能涉及更多的包装。 我认为你的解决scheme是一样整齐。

那么,如果方法期望一个IEnumerable你必须传递一个列表,即使它只包含一个元素。

通过

 new T[] { item } 

作为论据应该是足够的,我想

在C#3.0中,您可以使用System.Linq.Enumerable类:

 // using System.Linq Enumerable.Repeat(item, 1); 

这将创build一个只包含您的项目的新IEnumerable。

在C#3(我知道你说了2),你可以写一个通用的扩展方法,可能会使语法更容易被接受:

 static class IEnumerableExtensions { public static IEnumerable<T> ToEnumerable<T>(this T item) { yield return item; } } 

客户端代码然后是item.ToEnumerable()

我有点惊讶,没有人提出一个Ttypes的参数来简化客户端API的方法的新重载。

 public void DoSomething<T>(IEnumerable<T> list) { // Do Something } public void DoSomething<T>(T item) { DoSomething(new T[] { item }); } 

现在你的客户端代码可以做到这一点:

 MyItem item = new MyItem(); Obj.DoSomething(item); 

或者用一个列表:

 List<MyItem> itemList = new List<MyItem>(); Obj.DoSomething(itemList); 

正如我刚刚find的,并看到用户LukeH也build议,一个很好的简单的方法做到这一点如下:

 public static void PerformAction(params YourType[] items) { // Forward call to IEnumerable overload PerformAction(items.AsEnumerable()); } public static void PerformAction(IEnumerable<YourType> items) { foreach (YourType item in items) { // Do stuff } } 

这种模式将允许您以多种方式调用相同的function:单个项目; 多个项目(逗号分隔); 数组; 一个列表; 枚举等

尽pipe使用AsEnumerable方法的效率并不是100%确定的,但它确实有效。

更新:AsEnumerable函数看起来非常高效! ( 参考 )

要么(如前所述)

 MyMethodThatExpectsAnIEnumerable(new[] { myObject }); 

要么

 MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(myObject, 1)); 

作为一个侧面说明,最后一个版本也可以很好,如果你想要一个匿名对象的空列表,例如

 var x = MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(new { a = 0, b = "x" }, 0)); 

这可能不会更好,但它有点酷:

 Enumerable.Range(0, 1).Select(i => item); 

这个辅助方法适用于物品或许多物品。

 public static IEnumerable<T> ToEnumerable<T>(params T[] items) { return items; } 

我同意@EarthEngine对原帖的评论,这就是“AsSingleton”是个更好的名字。 看到这个维基百科条目 。 然后从单例的定义来看,如果一个空值作为一个parameter passing,那么'AsSingleton'应该返回一个具有单一空值的IEnumerable,而不是一个可以解决if (item == null) yield break;的空IEnumerable if (item == null) yield break; 辩论。 我认为最好的解决scheme是有两种方法:'AsSingleton'和'AsSingletonOrEmpty'; 其中,如果将null作为parameter passing,则“AsSingleton”将返回一个空值,而“AsSingletonOrEmpty”将返回一个空的IEnumerable。 喜欢这个:

 public static IEnumerable<T> AsSingletonOrEmpty<T>(this T source) { if (source == null) { yield break; } else { yield return source; } } public static IEnumerable<T> AsSingleton<T>(this T source) { yield return source; } 

然后,这些或多或less会类似于IEnumerable上的“First”和“FirstOrDefault”扩展方法,这种方法感觉不错。

由于这个C#编译器优化 ,在foreach使用时, 这比yieldEnumerable.Repeat快30%,而在其他情况下性能相同。

 public struct SingleSequence<T> : IEnumerable<T> { public struct SingleEnumerator : IEnumerator<T> { private readonly SingleSequence<T> _parent; private bool _couldMove; public SingleEnumerator(ref SingleSequence<T> parent) { _parent = parent; _couldMove = true; } public T Current => _parent._value; object IEnumerator.Current => Current; public void Dispose() { } public bool MoveNext() { if (!_couldMove) return false; _couldMove = false; return true; } public void Reset() { _couldMove = true; } } private readonly T _value; public SingleSequence(T value) { _value = value; } public IEnumerator<T> GetEnumerator() { return new SingleEnumerator(ref this); } IEnumerator IEnumerable.GetEnumerator() { return new SingleEnumerator(ref this); } } 

在这个testing中:

  // Fastest among seqs, but still 30x times slower than direct sum // 49 mops vs 37 mops for yield, or c.30% faster [Test] public void SingleSequenceStructForEach() { var sw = new Stopwatch(); sw.Start(); long sum = 0; for (var i = 0; i < 100000000; i++) { foreach (var single in new SingleSequence<int>(i)) { sum += single; } } sw.Stop(); Console.WriteLine($"Elapsed {sw.ElapsedMilliseconds}"); Console.WriteLine($"Mops {100000.0 / sw.ElapsedMilliseconds * 1.0}"); } 

IanG 在这个话题上有一个很好的post ,build议使用EnumerableFrom()作为名字,并提到讨论指出Haskell和Rx把它称为ReturnIIRC F#调用它也返回 。 F#的Seq调用操作符singleton<'T>

如果你准备成为以C#为中心的诱惑,就叫它Yield (这意味着实现它的yield return )。

如果你对它的performance感兴趣,詹姆斯·迈克尔·黑尔(James Michael Hare)也有一个归零或者一个物品的post ,这是非常值得一看的。

虽然对于一种方法来说是过度的,但我相信有些人可能会发现互动式扩展有用。

来自Microsoft的交互式扩展(Ix)包含以下方法。

 public static IEnumerable<TResult> Return<TResult>(TResult value) { yield return value; } 

可以这样使用:

 var result = EnumerableEx.Return(0); 

Ix增加了在原始Linq扩展方法中找不到的新function,并且是创build反应式扩展(Rx)的直接结果。

对于IEnumerable ,认为Linq Extension Methods + Ix = Rx

您可以在CodePlex上findRx和Ix 。

我想说的最简单的方法是new T[]{item}; ; 没有语法来做到这一点。 我能想到的最接近的是params关键字,但是这当然需要你有权访问方法定义,并且只能用于数组。