Assumed that I have a delimited text that contains 2 columns only: -
public DataField[] Read()
{
// splits the text according to the defined delimiters.
var data = _reader.Read();
// when no columns specified, take all column from data.
if (_columns == null)
{
var counter = 0;
// save as the instance fields to avoid recompilation.
_columns = data.Select(item => new ColumnInfo
{
Index = counter++
});
}
return data == null
? null
: _columns.Select(item => new DataField
{
Column = item,
Data = data[item.Index]
}).ToArray();
}
To my surprise that this Read method keeps throwing ArrayOutOfBoundException after the first attempt. Can anyone guess what’s wrong? :) (Answer: scroll down please)
After the first call, the enumerator is wellover (at its end element). However, IEnumerable created (which I thought it was) from a Linq statement is not a real creation of IEnumerable instance.
_columns = data.Select(item => new ColumnInfo
{
Index = counter++
});
This statement is rather associating _columns field to the Linq statement (like a callback, I think). The subsequent Read() call would continue to invoke the lambda expression within the linq statement for next element, which created column with Index=3. (This also explains why linq’s performance is good without much overhead).
To workaround this side effets, one need to really tell the Linq function to create a real array or list for caching and the side effect will be gone.
Solution as such: -
public DataField[] Read()
{
var data = _reader.Read();
if (_columns == null)
{
var counter = 0;
// ToArray() will tear off the callback by caching a real array, not callback.
_columns = data.Select(item => new ColumnInfo
{
Index = counter++
}).ToArray();
}
return data == null
? null
: _columns.Select(item => new DataField
{
Column = item,
Data = data[item.Index]
}).ToArray();
}
Read Jon Skeet article on Human Linq
Also here about side effects with select
Hi,
ReplyDeleteAs I come here quite often to get .NET coding tips I wonder if you could use a syntax highlighter - it'll make your sample code way easier to read. Here's one way to do it on Blogger: Gist. Thanks!
I have looked into GIST as you suggested, for some reason I can't get it to work here. Also, considering that has to depends to GIST availability (in case this company goes down), I have eventually resolve to use some long term solution by embedding some CSS into the template. Anyway, thanks for the suggestion. :)
ReplyDelete