4

I have an object with a dynamic array of strings which I've implemented as follows:

public class MyThing { public int NumberOfThings { get; set; } public string _BaseName { get; set; } public string[] DynamicStringArray { get { List<string> dsa = new List<string>(); for (int i = 1; i <= this.NumberOfThings; i++) { dsa.Add(string.Format(this._BaseName, i)); } return dsa.ToArray(); } } } 

I was trying to be a little cooler earlier and implement something that autocreated the formatted list of arrays in LINQ but I've managed to fail.

As an example of the thing I was trying:

int i = 1; // create a list with a capacity of NumberOfThings return new List<string>(this.NumberOfThings) // create each of the things in the array dynamically .Select(x => string.Format(this._BaseName, i++)) .ToArray(); 

It's really not terribly important in this case, and performance-wise it might actually be worse, but I was wondering if there was a cool way to build or emit an array in LINQ extensions.

3
  • 7
    I say return IEnumerable<string> then, not string[]. Commented Sep 14, 2015 at 14:43
  • 3
    Emit a list... yet you use ToArray (not ToList()) and then cast to a List<string> ... why not use ToList() then you won't need the cast (as you are constructing a string in the select)? Commented Sep 14, 2015 at 14:49
  • Hey Paul, apologies, I've cleaned up the references to "list" I misspoke. I actually did want an array, but erroneously used list and array as synonyms (even though I'm fully aware that they're different objects). Sorry about the confusion. Backs' answer was what I was looking for. Commented Sep 14, 2015 at 22:21

3 Answers 3

11

Will Range help?

return Enumerable .Range(1, this.NumberOfThings) .Select(x => string.Format(this._BaseName, x)) .ToArray(); 
Sign up to request clarification or add additional context in comments.

4 Comments

He wants a list, not an array.
@iheanyi really? in both examples i see .ToArray(). Anyway, just replace .ToArray() to .ToList() and you'll get it. Also, you can edit answers
Hmm, I can't make that edit - not enough text being changed. But you raise a good point regarding his examples.
@iheanyi - oops! I misspoke :) sorry for the confusion. I suppose I use List & Array interchangeably while talking to people but obviously use the right actual type when programming. Backs is correct, the final step of .ToList() or .ToArray() is trivial enough to be irrelevant while Enumerable.Range is the real "guts" of the answer.
1

Your property could return an IEnumerable and you could then invoke the ToArray() extension on that, if you needed to.

public string[] DynamicStringArray { get { for (int i=1; i <= this.NumberOfThings; i++) yield return string.Format(this._BaseName, i); } } 

However, yields are inherently slow because of the context switching that goes on. You're better off doing this:

public string[] DynamicStringArray { get { string[] result = new string[this.NumberOfThings]; for (int i = 0; i < this.NumberOfThings; i++) { result[i] = string.Format(this._BaseName, i + 1)); } return result; } } 

Those Linq extension methods are nice for when you're feeling lazy. But if you need it to perform well you should avoid them.

5 Comments

"context switching" - can you explain what you are talking about?
Each yield return switches from managed to native then back to managed. You can put a breakpoint on the yield return and watch what happens in the call stack.
@Lorek You might be not understanding it fully, please see e.g. csharpindepth.com/Articles/Chapter6/…
To my knowledge there is no manged-to-native-and-back transition when you call next on iterator. Yield return produces relatively simple managed code - the same as any other manged code. I'd recommend checking out stackoverflow.com/questions/742497/… and related links - it may clarify for you at least what is going on. If you still see native code somewhere - consider if asking question about it is useful for you.
Weird. I just tested it and it behaves as it should, which is not how I originally described. I can only guess that the compiler has changed since I last tested it (several years ago) because it used to switch context. The only context switching now is in managed code. And, although that is not as bad as what I thought was happening, it can still be avoided to improve performance.
1

I'd rather redesign a bit the current solution:

 public class MyThing { ... // Note IEnumerable<String> instead of String[] public IEnumerable<String> DynamicString(int numberOfThings) { if (numberOfThings < 0) throw new ArgumentOutOfRangeException("numberOfThings"); for (int i = 0; i < numberOfThings; ++i) yield return string.Format(this._BaseName, i + 1); } } 

whenever you want, say, an array you can easily obtain it:

 MyThing thing = ...; // Add .ToArray() to have an array String[] myArray = thing.DynamicString(18).ToArray(); 

but whenever all you want is just loop there's no need to create an array or list (materialize a result)

 // no materialization: no array of 1000000 items foreach (String item in thing.DynamicString(1000000)) { ... } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.