1

If we want a single IEnumerable<T> representing the concatenation of of two IEnumerable<T>s we can use the LINQ Concat() method.

For example:

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

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

of course outputs 12345.

My question is, why is there no overload of Concat() just accepting a single element of type T such that:

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

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

would compile and produce the output: 1234?

Googling around the issue throws up a couple of SO questions where the accepted answer suggests that the best approach when looking to achieve this is to simply do a.Concat(new int[] {4}). Which is fine(ish) but a little 'unclean' in my opinion because:

  • Maybe there is a performance hit from declaring a new array (albeit this is presumably going to be negligible pretty much evey time)
  • It just doesn't look as neat, easy to read and natural as a.Concat(4)

Anyone know why such an overload doesn't exist?

Also, assuming my Googling hasn't let me down - there is no such similar LINQ extension method taking a single element of type T.

(I understand it is trivially easy to roll one's own extension method to produce this effect - but doesn't that just make the omission even more odd? I suspect there will be a reason for it's omission but can't imagine what it could be?)

UPDATE:

Acknowledging the couple of votes to close this as opinion based - I should clarify that I am NOT seeking peoples opinions on whether this would be a good addition to LINQ.

More I am seeking to understand the FACTUAL reasons why it is not ALREADY part of LINQ.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Stewart_R
  • 13,764
  • 11
  • 60
  • 106
  • 3
    There doesn't have to be a reason for an omission. There does have to be a good reason for an inclusion. – H H May 03 '15 at 21:50
  • @HenkHolterman Fair point but IMHO - the couple of bullet points I mention provide that reason? No? – Stewart_R May 03 '15 at 21:52
  • [_"Every feature starts out in the hole by 100 points, which means that it has to have a significant net positive effect on the overall package for it to make it into the language."_](http://blogs.msdn.com/b/ericgu/archive/2004/01/12/57985.aspx) – James Thorpe May 03 '15 at 21:52
  • @HenkHolterman I cannot doubt your experience but surely real use-cases for this behaviour cannot be very uncommon? (I'm afraid i need to confess that I don't really understand the null-coalesence issue :-) ) – Stewart_R May 03 '15 at 22:06
  • 1
    Bottom line is: we can only guess. Unless someone from the C# language specification group chimes in. So: opinion based, 100%. And not relevant because we don't know. Guesses are no answers. – Gert Arnold May 03 '15 at 22:08
  • 1
    the FACTUAL reasons why it is not ALREADY part of LINQ can only be answered by the language designers. – Blorgbeard May 03 '15 at 22:09
  • @Blorgbeard - Actually that would be the Library designers. Extensions methods are not part of the language design. – H H May 03 '15 at 22:10
  • or people that have read documentation, blogs, announcments, etc from such a team which I havent been able to find- then those people could link that refenece and answer factually (as often happend on SO) – Stewart_R May 03 '15 at 22:11
  • @HenkHolterman I have no list except to say that i had a specific reason to look for this functionality which prompted the initial research (Happy to give you the details if you really want them but is a bit convoluted for these comments). Also this SO question suggests a lot of interest to a similar (if subtely different) problem: http://stackoverflow.com/questions/1210295/how-can-i-add-an-item-to-a-ienumerablet-collection – Stewart_R May 03 '15 at 22:17
  • @HenkHolterman Aknowledged. I wasnt citing it as an exemplary coding example - just highlighting that, given the volume of traffic and interest, that concatenating a single element is something others have found necesary or useful (however misguided) – Stewart_R May 03 '15 at 22:25
  • 2
    _"I am seeking to understand the FACTUAL reasons why it is not ALREADY part of LINQ."_ -- and how's that going for you? Six different answers have been posted so far, and **not one** includes any _factual_ information as to why this isn't part of LINQ. They are all _opinions_. And that is exactly why that close reason exists; because questions of this nature draw a lot of _opinions_, but no _facts_. – Peter Duniho May 03 '15 at 23:12
  • 1
    The factual reason is that nobody put it in. – Jon Hanna May 03 '15 at 23:55
  • How deeply frustrating. If I were to ask clearly, specifically and sensibly (on some suitable site) what the economic pressures were that effected the price of cheese and then other people responded with thier own reasons as to why they wished cheese was cheaper at the local store would you moderate the answers or the question? – Stewart_R May 04 '15 at 05:41
  • @Stewart_R The question is only closed because it's not on topic here. *Not because it's a bad question*. Of course it's a valid question. I still hold the belief that bad questions don't exist. But SO happens to have this concept of *answerable* questions. Now your question is answerable (by a very limited number of people), but it also provokes speculation, mainly because all answerers so far didn't read what you're asking. I'd happily have upvoted an authorized, factual answer. Experience learns however that opinion rules, so we end up with yet another pile of guesswork. – Gert Arnold May 04 '15 at 06:58
  • @GertArnold I appreciate that (and the answers so far, however interesting, certainally corroborate your experience). I can't help but be frustrated, however, as I really did check out the guidance and have read this: http://stackoverflow.com/help/dont-ask and re-read it and still feel like my question meets the rules and guidance. If my hypothesised documentation or blog exists then the first answer citing it will surely get upvoted past the current answers? Similarly if one of those small number of people happen along thier answer would too. Isn't that what's supposed to happen on SO? – Stewart_R May 04 '15 at 07:03
  • Sure, if someone would know the real answer s/he could comment here and we could start voting for reopening. I don't see many Microsoft people operating on SO though. – Gert Arnold May 04 '15 at 07:16
  • 1
    The reason to close the question is not because it is opinion based, but rather because the entire question is predicated on a faulty premise: that there is a decision process for not doing a feature. All features are unimplemented until someone thinks of them, then does a design, writes a spec, writes the code, tests the code, documents the feature and ships it to customers. None of those things happened for your proposed feature and that is the sole reason why there is no such feature. The design team does not *ever* have to provide a justification for *not* doing a feature. – Eric Lippert May 04 '15 at 14:04
  • When @EricLippert wades in to suggest your question should be closed maybe it's time to (however reluctantly!) climb down and admit defeat... Should I delete this or let it stand as closed? – Stewart_R May 04 '15 at 14:09
  • 2
    I don't have a strong opinion on that question. What I (frequently!) encourage people to do though is to not ask "why", or worse, "why not" questions on SO; it is very difficult to answer them satisfactorily. The answer to "why" is usually "the design team debated this for hours/days/weeks and we have no transcript of that conversation", and we've already seen that every "why not?" has the same answer: the feature started not-implemented and stayed there because everyone was busy with other features. Stick to "how" and "what" questions. – Eric Lippert May 04 '15 at 14:24
  • @Stewart_R Just noticed this on meta, it seems to bear some relevance to this question: http://meta.stackoverflow.com/questions/293815/is-it-subjective-to-ask-about-why-something-wasnt-implemented-in-the-language – Alex May 10 '15 at 03:49

4 Answers4

6

In .NET Framework 4.7.1 they added Prepend and Append methods to add one element to the beginning and to the end of enumerable correspondingly.

usage:

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

First - your Googling is fine - there is no such method. I like the idea though. It's a use case that if you run in to, having it would be great.

I suspect it wasn't included with the LINQ API because the designers didn't see a common enough need for it. That's just my conjecture though.

You're right to say that creating an array with just one element isn't all that intuitive. You can get the feel and performance you're going for with this:

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

Output:

0
1
2
3
4
  • Lazy evaluation
  • Implemented similarly to the built-in LINQ methods (they actually return an internal iterator, instead of directly yielding)
  • Argument checking wouldn't hurt it
jdphenix
  • 15,022
  • 3
  • 41
  • 74
1

A good reason for inclusion (in one form or another) would be for IEnumerables to be more like functional sequence monads.

But since LINQ did not arrive until .NET 3.0, and is implemented mostly using extension methods, I can imagine that they omitted extension methods working on a single element of T. Still this is pure speculation on my part.

They did however include generator functions, that are not extension methods. Specifically the following:

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

You could use these instead of homebrew extension methods. The two use cases you mentioned, can be solved as:

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

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

It might have been nice if an additional overload was included as syntactical sugar.

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

The absence of an explicit Unit function is curious and interesting

In the interesting MoreLINQ series of blog posts by Bart De Smet, illustrated using the System.Linq.EnumerableEx, the post More LINQ with System.Interactive – Sequences under construction specifically deals with this question, using the following appropriately named method for constructing a single element IEnumerable.

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

This is nothing but the return function (sometimes referred to as unit) used on a monad.

Also interesting is the blog series by Eric Lippert on monads, which features the following quote in part eight:

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

And frankly, that last one is a bit dodgy. I wish there was a static method on Enumerable specifically for making a one-element sequence.

Furthermore, the F# language provides the seq type:

Sequences are represented by the seq<'T> type, which is an alias for IEnumerable. Therefore, any .NET Framework type that implements System.IEnumerable can be used as a sequence.

It provides an explicit unit function as Seq.singleton.

Concluding

While none of this provides us with facts that shed light on the reasons why these sequence constructs are not explicitly present in c#, until someone with knowledge of the design decision process shares that information, it does highlight it would be worth knowing more about.

Alex
  • 13,024
  • 33
  • 62
  • 1
    _"It might have been nice if an additional overload was included as syntactical sugar"_ -- why? How would `Enumerable.FromElement(x)` be better than just `new [] { x }`? – Peter Duniho May 03 '15 at 22:30
  • @PeterDuniho I am not claiming that it is better. It would make the intent clear though: I want to instantiate an `IEnumerable` with a single element, instead of having to choose a specific `IEnumerable` implementation such as an array. The reasoning is not that different from the fact that I would prefer to use `IEnumerable.Empty` instead of `new int[0];` or `new int[] {}`, (independent of the static instance optimization reasons). Also one might benefit from potential future compiler technology optimizations for single element sequences, as the implementation is hidden from us. – Alex May 03 '15 at 22:57
  • @PeterDuniho, I added some additional information to my answer to clarify why it would not have been odd, if such a generator function existed, given that it is the most basic constructor for the `IEnumerable` monad. – Alex May 03 '15 at 23:41
  • @PeterDuniho: The former could not be cast to an array whose sole element could be mutated; the latter could. – Eric Lippert May 04 '15 at 14:29
-2

Philosophical questions, I like that.

At first, you can create easily that behaviour with an extension method

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

I think the central question is that IEnumerable is an immutable interface and it is not meant to be modified on the fly.

This could (I use could because I do not work in Microsoft so I may be completely wrong) be the reason while the modify part of IEnumerable is not so well developed (in the meaning that you're missing some handy methods).

If you have to modify that collection, consider to use a List or another interface.

Vincenzo
  • 1,549
  • 1
  • 9
  • 17
  • 1
    Why would concatenating a single element be any different to concatenating an entire series, with regard to modifying things? – James Thorpe May 03 '15 at 21:55
  • I am afraid I do not have a clear answer for that. The only reason I can find is that, while it may happen that you really have (exceptionally) concatenate 2 enumerable, adding a single element should never happen because IEnumerable is immutable. If you're adding a single element to an IEnumerable, perhaps you should consider an IList implementation. Probably Microsoft engineers want to _underline_ this concept omitting the single add method. Makes sense to you? – Vincenzo May 03 '15 at 21:58