LINQ连接一个额外的元素

dfty9e19  于 2023-04-27  发布在  其他
关注(0)|答案(4)|浏览(140)

如果我们想要一个IEnumerable<T>表示两个IEnumerable<T>的连接,我们可以使用LINQ Concat()方法。
例如:

int[] a = new int[] { 1, 2, 3 };
int[] b = new int[] { 4, 5 };

foreach (int i in a.Concat(b))
{
    Console.Write(i);
}

当然输出12345
我的问题是,为什么Concat()没有重载,只是接受了一个T类型的元素,使得:

int[] a = new int[] { 1, 2, 3 };

foreach (int i in a.Concat(4))
{
    Console.Write(i);
}

将编译并生成输出:1234
在谷歌上搜索这个问题会产生一些SO问题,其中公认的答案表明,在寻求实现这一目标时,最好的方法是简单地执行a.Concat(new int[] {4})。这很好(ish),但在我看来有点“不干净”,因为:

  • 也许声明一个新的数组会对性能造成影响(尽管这可能几乎每次都可以忽略不计)
  • 它看起来不像a.Concat(4)那样整洁、易读和自然

有人知道为什么这样的超载不存在吗?
另外,假设我的Google搜索没有让我失望--没有类似的LINQ扩展方法接受类型为T的单个元素。
(我知道这是很容易滚动自己的扩展方法来产生这种效果-但这不只是使遗漏更奇怪?我怀疑会有一个原因,它的遗漏,但不能想象它可能是什么?)
更新:
承认这对夫妇的投票结束这作为意见为基础-我应该澄清,我不是在征求人们的意见,这是否将是一个很好的除了LINQ。
更多的我正在寻求理解为什么它不是LINQ的一部分的事实原因。

mwyxok5s

mwyxok5s1#

在.NET Framework 4.7.1中,他们添加了PrependAppend方法,以将一个元素相应地添加到enumerable的开头和结尾。
使用方法:

var emptySequence = Enumerable.Empty<long>();
var singleElementSequence = emptySequence.Append(256L);
q9rjltbz

q9rjltbz2#

一个很好的理由是IEnumerables更像函数序列单子。
但是由于LINQ直到.NET 3.0才出现,并且主要使用扩展方法实现,我可以想象他们省略了对T的单个元素的扩展方法。
然而,它们确实包含了生成器函数,而不是扩展方法。具体如下:

  • Enumerable.Empty
  • Enumerable.Repeat
  • Enumerable.Range

您可以使用这些方法来代替自制扩展方法。您提到的两个用例可以解决为:

int[] a = new int[] { 1, 2, 3 };

var myPrependedEnumerable = Enumerable.Repeat(0, 1).Concat(a);
var myAppendedEnumerable = a.Concat(Enumerable.Repeat(4, 1));

如果包含一个额外的重载作为语法糖,可能会更好。

Enumerable.FromElement(x); // or a better name (see below).

没有明确的Unit函数是奇怪和有趣的

在Bart德斯梅特的博客文章MoreLINQ series中,使用System.Linq.EnumerableEx进行了说明,More LINQ with System.Interactive – Sequences under construction帖子专门处理了这个问题,使用以下适当命名的方法来构造单个元素IEnumerable

public static IEnumerable<TSource> Return<TSource>(TSource value);

这只不过是在 monad 上使用的返回函数(有时称为 unit)。
同样有趣的是Eric Lippert关于monads的博客系列,其中引用了part eight中的以下内容:

IEnumerable<int> sequence = Enumerable.Repeat<int>(123, 1);

坦率地说,最后一个有点不可靠,我希望Enumerable上有一个静态方法专门用于生成一个单元素序列。
此外,F#语言提供了seq类型:
序列由seq<'T>类型表示,它是IEnumerable的别名。因此,任何实现System.IEnumerable的.NET Framework类型都可以用作序列。
它提供了一个显式的unit函数Seq.singleton

结束

虽然这些都没有为我们提供“事实”来阐明这些序列结构在c#中没有显式存在的原因,但直到有设计决策过程知识的人分享这些信息,它确实突出了它值得了解更多。

y1aodyip

y1aodyip3#

首先-你的谷歌搜索是罚款-没有这样的方法。我喜欢这个想法虽然。这是一个用例,如果你遇到,有它将是伟大的。
我怀疑它没有包含在API中是因为设计者没有看到对它的足够普遍的需求。
你说的对,创建一个只有一个元素的数组并不是那么直观。你可以通过下面的代码获得你想要的感觉和性能:

public static class EnumerableExtensions {
   public static IEnumerable<T> Concat<T>(this IEnumerable<T> source, T element) {
        foreach (var e in source) {
            yield return e; 
        }
        yield return element;
    }

    public static IEnumerable<T> Concat<T>(this T source, IEnumerable<T> element) {
        yield return source; 
        foreach (var e in element) {
            yield return e;
        }

    }
}

class Program
{
    static void Main()
    {
        List<int> ints = new List<int> {1, 2, 3}; 
        var startingInt = 0; 

        foreach (var i in startingInt.Concat(ints).Concat(4)) {
            Console.WriteLine(i);
        }
    }
}

输出:

0
1
2
3
4
  • 惰性评估
  • 实现方式与内置LINQ方法类似(它们实际上返回一个内部迭代器,而不是直接产生)
  • 参数检查不会有什么坏处
gijlo24d

gijlo24d4#

哲学问题,我喜欢。
首先,您可以使用扩展方法轻松地创建该行为

public static IEnumerable<TSource> Concat(this IEnumerable<TSource> source, TSource element)
{
   return source.Concat(new[]{element});
}

我认为核心问题是IEnumerable是一个不可变的接口,它不应该被动态修改。
这可能(我使用could,因为我不在微软工作,所以我可能完全错了)是IEnumerable的modify部分不是很好开发的原因(这意味着你错过了一些方便的方法)。
如果必须修改该集合,请考虑使用List或其他接口。

相关问题