Gro*_*roo 357 .net c# generics ienumerable
是否有一种常见的方法将单个类型的项传递给T需要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;
}
}
Run Code Online (Sandbox Code Playgroud)
其他方式当然是创建和填充a List<T>或an Array而不是传递它IEnumerable<T>.
[编辑]作为扩展方法,它可能被命名为:
public static class IEnumerableExt
{
// usage: someObject.SingleItemAsEnumerable();
public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item)
{
yield return item;
}
}
Run Code Online (Sandbox Code Playgroud)
我在这里错过了什么吗?
[Edit2]我们发现someObject.Yield()(正如@Peter在下面的评论中所建议的那样)是这个扩展方法的最佳名称,主要是为了简洁,所以如果有人想要抓住它的话,它会与XML注释一起:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
Mar*_*o F 123
好吧,如果方法期望IEnumerable你必须传递一个列表,即使它只包含一个元素.
通过
new T[] { item }
Run Code Online (Sandbox Code Playgroud)
因为论证应该足够我想
luk*_*san 111
在C#3.0中,您可以使用System.Linq.Enumerable类:
// using System.Linq
Enumerable.Repeat(item, 1);
Run Code Online (Sandbox Code Playgroud)
这将创建一个仅包含您的项目的新IEnumerable.
Jon*_*eet 95
你的助手方法是最干净的方法,IMO.如果你传入一个列表或一个数组,那么一段不道德的代码可能会抛出它并改变内容,在某些情况下会导致奇怪的行为.您可以使用只读集合,但这可能涉及更多包装.我认为你的解决方案尽可能整洁.
Ðаn*_*Ðаn 31
在C#3中(我知道你说过2),你可以编写一个通用的扩展方法,它可以使语法更容易被接受:
static class IEnumerableExtensions
{
public static IEnumerable<T> ToEnumerable<T>(this T item)
{
yield return item;
}
}
Run Code Online (Sandbox Code Playgroud)
然后是客户端代码item.ToEnumerable().
dur*_*aid 11
这个辅助方法适用于项目或许多项目.
public static IEnumerable<T> ToEnumerable<T>(params T[] items)
{
return items;
}
Run Code Online (Sandbox Code Playgroud)
我很惊讶没有人建议使用类型为T的参数重新设置方法,以简化客户端API.
public void DoSomething<T>(IEnumerable<T> list)
{
// Do Something
}
public void DoSomething<T>(T item)
{
DoSomething(new T[] { item });
}
Run Code Online (Sandbox Code Playgroud)
现在您的客户端代码可以执行此操作:
MyItem item = new MyItem();
Obj.DoSomething(item);
Run Code Online (Sandbox Code Playgroud)
或列表:
List<MyItem> itemList = new List<MyItem>();
Obj.DoSomething(itemList);
Run Code Online (Sandbox Code Playgroud)
对于那些想知道性能的人,虽然@mattica在上面提到的类似问题中提供了一些基准测试信息,但是我的基准测试提供了不同的结果。
在 .NET 7 中,yield return value速度比 9% 快new T[] { value },并分配 75% 的内存量。在大多数情况下,这已经是超高性能的并且可以满足您的需要。
我很好奇自定义单个集合实现是否会更快或更轻量。事实证明,因为yield return被实现为IEnumerator<T>和IEnumerable<T>,所以在分配方面击败它的唯一方法就是在我的实现中也这样做。
如果您要转到IEnumerable<>外部图书馆,我强烈建议您不要这样做,除非您非常熟悉您正在构建的内容。话虽这么说,我做了一个非常简单(不重用安全)的实现,它能够比yield方法快5ns,并且只分配了数组的一半。
因为所有测试都通过了IEnumerable<T>,所以值类型的性能通常比引用类型差。我的最佳实现实际上是最简单的 - 您可以查看我链接到的要点SingleCollection中的类。(这比 快 2 纳秒,但分配了数组的 88%,而为 分配了 75% 。)yield returnyield return
TL:博士;如果您关心速度,请使用yield return item. 如果您确实关心速度,请使用SingleCollection.
正如我刚刚发现的,并且看到用户LukeH也提出了这样做,一个很简单的方法是:
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
}
}
Run Code Online (Sandbox Code Playgroud)
此模式允许您以多种方式调用相同的功能:单个项目; 多个项目(以逗号分隔); 数组; 一个列表; 枚举等
我不是百分之百确定使用AsEnumerable方法的效率,但它确实有效.
更新:AsEnumerable函数看起来非常有效!(参考)
我同意@EarthEngine 对原帖的评论,即“AsSingleton”是一个更好的名字。请参阅此维基百科条目。然后根据单例的定义,如果将空值作为参数传递,则 'AsSingleton' 应该返回一个带有单个空值的 IEnumerable,而不是一个空的 IEnumerable,这将解决if (item == null) yield break;争论。我认为最好的解决方案是有两种方法:“AsSingleton”和“AsSingletonOrEmpty”;其中,如果 null 作为参数传递,“AsSingleton”将返回单个 null 值,“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;
}
Run Code Online (Sandbox Code Playgroud)
然后,这些或多或少类似于 IEnumerable 上的“First”和“FirstOrDefault”扩展方法,感觉正确。
虽然对于一种方法来说太过分了,但我相信有些人可能会发现Interactive Extensions非常有用.
Microsoft的Interactive Extensions(Ix)包括以下方法.
public static IEnumerable<TResult> Return<TResult>(TResult value)
{
yield return value;
}
Run Code Online (Sandbox Code Playgroud)
哪个可以这样使用:
var result = EnumerableEx.Return(0);
Run Code Online (Sandbox Code Playgroud)
Ix添加了原始Linq扩展方法中没有的新功能,并且是创建Reactive Extensions(Rx)的直接结果.
想想,Linq Extension Methods+ Ix= Rxfor IEnumerable.
您可以在CodePlex上找到Rx和Ix.
要么(如前所述)
MyMethodThatExpectsAnIEnumerable(new[] { myObject });
Run Code Online (Sandbox Code Playgroud)
要么
MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(myObject, 1));
Run Code Online (Sandbox Code Playgroud)
作为旁注,如果你想要一个匿名对象的空列表,最后一个版本也可以很好,例如
var x = MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(new { a = 0, b = "x" }, 0));
Run Code Online (Sandbox Code Playgroud)
IanG有一篇关于该主题的好帖子,建议EnumerableFrom()使用该名称(并提到 Haskell 和 Rx 称其为Return)。IIRC F# 也将其称为 Return。F#Seq调用 operatorsingleton<'T>。
如果您准备好以 C# 为中心,那么可以称之为Yield[暗指yield return实现它的过程]。
如果您对它的性能方面感兴趣,James Michael Hare 也有返回零或一个项目的帖子,非常值得一看。
这比快30%,yield或Enumerable.Repeat在使用时,foreach由于这个C#编译器的优化,而在其他情况相同的性能.
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);
}
}
Run Code Online (Sandbox Code Playgroud)
在这个测试中:
// 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}");
}
Run Code Online (Sandbox Code Playgroud)
我最近在另一篇文章中问了同样的事情
有没有办法调用需要 IEnumerable<T> 且具有单个值的 C# 方法?...通过基准测试。
我希望人们在这里停下来看看最新帖子中显示的针对这些答案中提出的 4 种方法的简短基准比较。
看来,简单地写入new[] { x }方法的参数是最短、最快的解决方案。
| 归档时间: |
|
| 查看次数: |
78368 次 |
| 最近记录: |