2

Why doesn't System.String include a constructor capable of taking a IEnumerable<char>?

The expected behavior would be:

var foo = "hello";
var bar = new string(foo.Select(x => x));

Actual behavior:

Cannot convert from 'System.Collections.Generic.IEnumerable<char>' to 'char*'

If there is no obvious reason, I think it could be a nice pull request.

source: String.cs StringNative.cpp

4
  • 1
    Unfortunately, the only answer we can provide to "Why did {somebody} do {something} instead of {my way}?" is "You have to ask {somebody}". In this case, {somebody} would be Microsoft. – Adam Zuckerman Jul 6 '16 at 20:28
  • @AdamZuckerman maybe there is a public issue somewhere (I search on github without success) or a softies who talk about this on a blog post) – aloisdg Jul 6 '16 at 20:37
  • I am not aware of any discussion or blog about why this wasn't included. In addition to Robert Harvey's answer, you could also write an extension method to take an IEnumerable<char> as a parameter. I suspect the implementation won't be significantly different that presented in his answer. – Adam Zuckerman Jul 6 '16 at 20:44
  • @AdamZuckerman "I suspect the implementation won't be significantly different" So do I.I find the absence of this constructor not intuitive. thats all. – aloisdg Jul 6 '16 at 20:48
6

Since there is a constructor overload that takes an array of characters, this should work:

var bar = new string(foo.Select(x => x).ToArray());

Which pretty much eliminates the need for another constructor overload, as the proposed overload would essentially have to do the same thing.

Eric Lippert often discusses why certain features don't make it into the .NET Framework or the C# language. He says:

The answer is always the same: because no one ever designed, specified, implemented, tested, documented and shipped that feature. All six of those things are necessary to make a feature happen. All of them cost huge amounts of time, effort and money.

In other words, every feature must have benefits that exceed those costs, and the .NET Team decided in this instance that the extra constructor was not worth it.

Further Reading
Best way to convert IEnumerable to string?

8
  • 2
    Which costs an additional allocation and memory copy compared to a native implementation for this, which for larger strings could be expensive. – CodesInChaos Jul 6 '16 at 20:33
  • Of course. But since it's not actually a string that is being passed... – Robert Harvey Jul 6 '16 at 20:34
  • So the answer would be that it doesn't exist because there is no need for it? Like a lot of features, I think this one would be convenient. – aloisdg Jul 6 '16 at 20:40
  • @aloisdg: I've added some clarification to my answer. – Robert Harvey Jul 6 '16 at 20:44
  • 1
    @CodesInChaos - note that as the size of an IEnumerable cannot (reliably) be determined in advance, any implementation of this feature is likely to feature multiple redundant allocation and copy operations anyway as the code will need to allocate an array as a buffer and resize it as characters are collected and it fills up. Perhaps the CLR designers considered the possibility of including this method and decided that the benefits of doing so outweighed the costs of development and support. – Periata Breatta Sep 8 '16 at 17:32
2

This constructor takes an array of chars as its input. For your use case, you can simply:

var bar = new string(foo.Select(x => x).ToArray());

If you wanted a rationale, you could say that the lazy-loading behavior of the IEnumerable does not give you any added benefit here since you need to read the whole thing anyway to create the string. Whether you convert it to an array before you pass it, as my example does, or pass it an IEnumerable for the constructor to convert, the operations and performance should be almost the same.

7
  • So why List<T> (or any other collection) has a constructor taking an IEnumerable<T>? – aloisdg Jul 6 '16 at 20:42
  • @aloisdg: List<T> already inherits from the IEnumerable<T> interface, making the constructor implementation trivial. – Robert Harvey Jul 6 '16 at 20:49
  • @RobertHarvey String inherits from IEnumerable<char>. I guess making the constructor is trivial too. – aloisdg Jul 6 '16 at 20:50
  • That's not IEnumerable<T>; it's IEnumerable<char>. There's a difference, and it's a material one. – Robert Harvey Jul 6 '16 at 20:51
  • @RobertHarvey True, but I don't see why it is a problem. – aloisdg Jul 6 '16 at 20:52
1

On the one hand we expect convenience, on the other strict OOP principles being applied. If you implement too many convenient overloads, you will soon be implementing a universal type converter in a string class. And then something else. I guess keeping things mean and lean and sticking to the single responsibility principle outweighed the need for more convenience. As others pointed out, there is not really an inconvenience in using ToArray on the argument. It is about putting responsibilities where they belong.

1
  • 1
    So "simplicity"? Any source for this? – aloisdg Jul 6 '16 at 21:18
0

The answer is simple: It wasn't modified when generics were introduced. There is a constructor that takes an array of char, and if it was being written today, it would undoubtedly take an IEnumerable instead, but in order to introduce that after the fact, would require a lot of testing and validation to make sure that there wasn't a regression and that the performance didn't degrade. For something that can be typically be resolved by adding .ToArray(). Sure, a lot more people have to do the .ToArray(), but there's no prior commitment to that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.