Passing a single item as IEnumerable<T>
有没有一种常见的方法将类型为
目前我使用的是一个助手方法(它是.NET 2.0,所以我有很多类似于Linq的强制转换/投影助手方法),但这看起来很愚蠢:
1 2 3 4 5 6 7 8 | public static class IEnumerableExt { // usage: IEnumerableExt.FromSingleItem(someObject); public static IEnumerable<T> FromSingleItem<T>(T item) { yield return item; } } |
另一种方法当然是创建和填充一个
[编辑]作为扩展方法,它可能命名为:
1 2 3 4 5 6 7 8 | public static class IEnumerableExt { // usage: someObject.SingleItemAsEnumerable(); public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item) { yield return item; } } |
我是不是错过了什么?
[edit2]我们发现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public static class IEnumerableExt { /// <summary> /// Wraps this object instance into an IEnumerable<T> /// 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<T> consisting of a single item. </returns> public static IEnumerable<T> Yield<T>(this T item) { yield return item; } } |
好吧,如果该方法需要一个
经过
1 |
我认为这场争论应该足够了
在C 3.0中,可以使用System.Linq.Enumerable类:
1 2 3 | // using System.Linq Enumerable.Repeat(item, 1); |
这将创建一个只包含您的项的新IEnumerable。
你的helper方法是最干净的方法,imo。如果你传入一个列表或数组,那么一段不道德的代码可以强制转换它并更改内容,在某些情况下会导致奇怪的行为。您可以使用只读集合,但这可能涉及更多包装。我认为你的解决方案是最完美的。
在C 3中(我知道你说过2),你可以编写一个通用的扩展方法,它可以让语法更容易接受:
1 2 3 4 5 6 7 | static class IEnumerableExtensions { public static IEnumerable<T> ToEnumerable<T>(this T item) { yield return item; } } |
客户端代码是
此助手方法适用于项或多个项。
1 2 3 4 | public static IEnumerable<T> ToEnumerable<T>(params T[] items) { return items; } |
我有点惊讶,没有人建议使用带有t类型参数的方法的新重载来简化客户端API。
1 2 3 4 5 6 7 8 9 | public void DoSomething<T>(IEnumerable<T> list) { // Do Something } public void DoSomething<T>(T item) { DoSomething(new T[] { item }); } |
现在,您的客户机代码可以这样做:
1 2 |
或列表:
1 2 |
正如我刚刚发现的,并且看到用户lukeh也建议的那样,一个很好的简单方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 } } |
此模式允许您以多种方式调用相同的功能:单个项目;多个项目(逗号分隔);数组;列表;枚举等。
不过,我不能百分之百地肯定使用无数法的效率,但它确实起到了治疗作用。
更新:AsEnumeratable函数看起来很有效!(参考)
虽然一种方法的杀伤力太大,但我相信有些人可能会发现交互式扩展很有用。
微软的交互式扩展(ix)包括以下方法。
1 2 3 4 | public static IEnumerable<TResult> Return<TResult>(TResult value) { yield return value; } |
可以这样使用:
1 | var result = EnumerableEx.Return(0); |
ix添加了在原始LINQ扩展方法中找不到的新功能,这是创建反应式扩展(RX)的直接结果。
想想,
你可以在codeplex上找到rx和ix。
型
由于此C编译器优化,在其他情况下性能相同,因此在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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); } } |
在本测试中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // 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}"); } |
号
或者(如前所述)
1 |
或
1 | MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(myObject, 1)); |
作为补充说明,如果您想要一个匿名对象的空列表,最后一个版本也可以很好。
1 |
我同意@outengine对原始帖子的评论,即"assingleton"是一个更好的名字。看看这个维基百科条目。然后,根据singleton的定义,如果将一个空值作为参数传递,那么"assingleton"应该返回一个具有单个空值的ienumerable,而不是一个空的ienumerable,这将解决
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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; } |
然后,这些方法或多或少类似于IEnumerable上的"First"和"FirstOrDefault"扩展方法,这只是感觉正确而已。
这可能不会更好,但有点酷:
1 | Enumerable.Range(0, 1).Select(i => item); |
梁在这个问题上有一个很好的帖子,建议以iirc f calls it return too.。f的
如果你准备好以"C"为中心,那就把它称为"EDOCX1"(8),[暗示着参与实现它的
如果你对它的性能方面感兴趣,詹姆斯迈克尔黑尔也有一个返回零或一个项目张贴太值得扫描。
我想说的最简单的方法是
型
我参加聚会有点晚了,但无论如何我还是要和你分享我的路。我的问题是我想将itemsource或wpf treeview绑定到单个对象。层次结构如下:
项目>地块>房间
总是只有一个项目,但我仍然希望在树中显示该项目,而不必像某些建议的那样传递一个集合,其中只包含一个对象。因为您只能将IEnumerable对象作为itemsource传递,所以我决定将类设置为IEnumerable:
1 2 3 4 5 6 7 8 9 10 | public class ProjectClass : IEnumerable<ProjectClass> { private readonly SingleItemEnumerator<AufmassProjekt> enumerator; ... public IEnumerator<ProjectClass > GetEnumerator() => this.enumerator; IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); } |
并相应地创建我自己的枚举器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | public class SingleItemEnumerator : IEnumerator { private bool hasMovedOnce; public SingleItemEnumerator(object current) { this.Current = current; } public bool MoveNext() { if (this.hasMovedOnce) return false; this.hasMovedOnce = true; return true; } public void Reset() { } public object Current { get; } } public class SingleItemEnumerator<T> : IEnumerator<T> { private bool hasMovedOnce; public SingleItemEnumerator(T current) { this.Current = current; } public void Dispose() => (this.Current as IDisposable).Dispose(); public bool MoveNext() { if (this.hasMovedOnce) return false; this.hasMovedOnce = true; return true; } public void Reset() { } public T Current { get; } object IEnumerator.Current => this.Current; } |
。
这可能不是"最干净"的解决方案,但对我很有效。
编辑为了坚持@groo指出的单一责任原则,我创建了一个新的包装类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public class SingleItemWrapper : IEnumerable { private readonly SingleItemEnumerator enumerator; public SingleItemWrapper(object item) { this.enumerator = new SingleItemEnumerator(item); } public object Item => this.enumerator.Current; public IEnumerator GetEnumerator() => this.enumerator; } public class SingleItemWrapper<T> : IEnumerable<T> { private readonly SingleItemEnumerator<T> enumerator; public SingleItemWrapper(T item) { this.enumerator = new SingleItemEnumerator<T>(item); } public T Item => this.enumerator.Current; public IEnumerator<T> GetEnumerator() => this.enumerator; IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); } |
我用这个
1 |
。
编辑2我用