0

GetEnumerator cast to an Interface fails
No compiler error
Runtime failure with message index infinity
If I use the struct Word1252 directly with no interface it works

namespace WordEnumerable
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            WordEnum wordEnum = new WordEnum();
            Debug.WriteLine(wordEnum[0].GetType().ToString());
            Debug.WriteLine(wordEnum[0].Value);
            Debug.WriteLine(wordEnum.Count.ToString());
            foreach (iWord w in wordEnum)  // fails here
            {
            }
        }
    }
}
public interface iWord
{
    Int32 Key { get; }
    String Value { get; }
}
public class WordEnum : IEnumerable<iWord>
{
    private static List<Word1252> words1252 = new List<Word1252>();
    IEnumerator<iWord> IEnumerable<iWord>.GetEnumerator()
    {
        return ((IEnumerable<iWord>)words1252).GetEnumerator();
    }
    public struct Word1252 : iWord
    {
        public UInt64 packed;
        public Int32 Key   { get { return (Int32)((packed >> 27) & ((1 << 25) - 1)); } }
        public Byte Length { get { return (Byte) ((packed >> 59) & ((1 <<  5) - 1)); } }
        public String Value { get { return Key.ToString(); } }
        public Word1252(UInt64 Packed) { packed = Packed; }
    }
paparazzo
  • 44,497
  • 23
  • 105
  • 176

1 Answers1

1

In short that's upcasting from IEnumerable<Word1252> to IEnumerable<IWord>,

And that requires covariance to work.

Even though IEnumerable is marked as out (covariant)
Covariance doesn't work for 'value types' - i.e. the struct you have the interface defined.

e.g. see...

Is this a covariance bug in C# 4?
(or a bit different but boils down to the same issue)
Why cannot IEnumerable<struct> be cast as IEnumerable<object>?

To resolve you can just define your list like

private static List<iWord> words1252 = new List<iWord>();  

Or define your enumerator like this:

IEnumerator<iWord> IEnumerable<iWord>.GetEnumerator()
{
    foreach (var word in words1252)
        yield return word;
}
Community
  • 1
  • 1
NSGaga-mostly-inactive
  • 14,052
  • 3
  • 41
  • 51
  • They both worked thanks. With the private static List words1252 = new List(); I had to cast to Word1252 for internal stuff but I will take that. Do you know if foreach has a performance penalty? If not I would rather use that. – paparazzo Apr 21 '13 at 00:15
  • you're welcome. `foreach` is nicer I agree. There is none performance impact there (or some op extra at most) - it's 'yielding' as it goes - so it's basically doing the same as the original 'enumerator' - that's a known and accepted method of 'casting' within `Enumerators' (i.e. when you need to return different item - but are unable to cast) – NSGaga-mostly-inactive Apr 21 '13 at 00:18