Maybe that’s a silly question… But what’s the best (performance and memory wise) way of creating a constant IEnumerable<TSomeType>…?
If it’s not possible to define “the best” way, which are my options? What is your opinion, do you think there is a most appropriate way of doing that?
For instance:
var enumerable = (IEnumerable<TSomeType>) new List<TSomeType> { Value1, Value2, Value3 };var enumerable = (IEnumerable<TSomeType>) new TSomeType[] { Value1, Value2, Value3 };- (some other option; for instance a Linq Select).
Please consider that memory and performance are an issue here – we’re talking about a really constrained environment (a small device with .NET installed).
Thanks in advance.
Well, neither
List<T>nor arrays are immutable, so they’re out if you’re really after immutability – the caller could cast the result and then modify it.You could create a
List<T>and wrap that in aReadOnlyCollection<T>. If nothing has a reference to the original list any more, then it’s effectively immutable, barring reflection.If you don’t actually care about immutability – i.e. if you trust all the code not to mess with it – then an array is going to be the most performant approach, almost certainly. There are various CLR-level optimizations which make them work blazingly fast. However, in that case I wouldn’t cast to
IEnumerable<T>– I’d just expose it as an array. That will make it faster to iterate over than if the compiler has to callGetEnumerator().If the C# compiler sees a
foreachstatement over an array, it generates calls to go straight to the indexer and use theLengthproperty… and then the CLR will also be able to remove bounds checking, spotting the pattern.Likewise if you decide to go with
List<T>, leave it as aList<T>– that way you’ll get to useList<T>.Enumerator– which is a struct – directly, without boxing.EDIT: Steve Megson brings up the point of using LINQ for this. Actually, you can probably do better than that, because once you’ve got the enumerator of the underlying list, you can give that back to the caller safely, at least for all collections I’m aware of. So you could have:
That means there’s only a tiny hit when iterating – just the single delegated call to
GetEnumerator(). Compare that with usingEnumerable.Select, which will need to take an extra delegation hit on every call toMoveNext()(as well as the no-op projection).