是否有一种通用的方法将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; 
    }
}

当然,另一种方法是创建并填充一个List<T>或一个Array,并传递它而不是IEnumerable<T>。

[编辑]作为一个扩展方法,它可以命名为:

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

我是不是遗漏了什么?

[Edit2]我们发现someObject.Yield()(正如@Peter在下面的评论中建议的那样)是这个扩展方法的最佳名称,主要是为了简洁,所以如果有人想获取它,这里是它和XML注释:

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;
    }
}

当前回答

I agree with @EarthEngine's comments to the original post, which is that 'AsSingleton' is a better name. See this wikipedia entry. Then it follows from the definition of singleton that if a null value is passed as an argument that 'AsSingleton' should return an IEnumerable with a single null value instead of an empty IEnumerable which would settle the if (item == null) yield break; debate. I think the best solution is to have two methods: 'AsSingleton' and 'AsSingletonOrEmpty'; where, in the event that a null is passed as an argument, 'AsSingleton' will return a single null value and 'AsSingletonOrEmpty' will return an empty IEnumerable. Like this:

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'扩展方法,这感觉是对的。

其他回答

如果方法需要一个IEnumerable,你就必须传递一个列表,即使它只包含一个元素。

通过

new[] { item }

我认为这个论点应该足够了

这比yield或Enumerable快30%。由于这种c#编译器优化,在foreach中重复使用,在其他情况下也具有相同的性能。

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);
    }
}

在这个测试中:

    // 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}");
    }

被归为“不一定是一个好的解决方案,但仍然……”一个解决方案”或“愚蠢的LINQ技巧”,你可以结合Enumerable.Empty<>()和Enumerable.Append<>()…

IEnumerable<string> singleElementEnumerable = Enumerable.Empty<string>().Append("Hello, World!");

...或Enumerable.Prepend < >()……

IEnumerable<string> singleElementEnumerable = Enumerable.Empty<string>().Prepend("Hello, World!");

后两种方法从。net Framework 4.7.1和。net Core 1.0开始就可用了。

如果真的打算使用现有方法而不是编写自己的方法,这是一个可行的解决方案,尽管我不确定这是否比Enumerable.Repeat<>()解决方案更清楚或更不清楚。这肯定是较长的代码(部分原因是Empty<>()不可能进行类型参数推断),并且创建了两倍的枚举器对象。

为了解决这个“你知道这些方法存在吗?”的问题,Array.Empty<>()可以替换为Enumerable.Empty<>(),但很难说这样做会使情况好转。

在c# 3.0中,你可以使用System.Linq.Enumerable类:

// using System.Linq

Enumerable.Repeat(item, 1);

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

我认为最简单的方法是new T[]{item};;没有语法来做这个。我能想到的最类似的是params关键字,但当然,它要求您能够访问方法定义,并且只能用于数组。