I know this code does not work (and have no problems writing it in a way that will work). I was wondering how the compiler can build with out any errors. And you get run time errors if you where to run it? ( assuming data was not null )
using System;
using System.Collections.Generic;
public class Class1
{
public void Main()
{
IEnumerable<IEnumerable<Foo>> data = null;
foreach(Foo foo in data){
foo.Bar();
}
}
}
public class Foo {
public void Bar() { }
}
After language 'B', Dennis Ritchie came up with another language which was based upon 'B'. As in alphabets B is followed by C and hence he called this language as 'C'.
C is a general-purpose programming language and can efficiently work on enterprise applications, games, graphics, and applications requiring calculations, etc. C language has a rich library which provides a number of built-in functions. It also offers dynamic memory allocation.
Because C comes after B The reason why the language was named “C” by its creator was that it came after B language. Back then, Bell Labs already had a programming language called “B” at their disposal.
This is because foreach
does not do compile time checking in your specific case. If you built working code you get a InvalidCastException
at run-time.
using System.Collections.Generic;
public class Test
{
internal class Program
{
public static void Main()
{
var item = new Foo();
var inner = new List<Foo>();
var outer = new List<List<Foo>>();
inner.Add(item);
outer.Add(inner);
IEnumerable<IEnumerable<Foo>> data = outer;
foreach (Foo foo in data)
{
foo.Bar();
}
}
}
public class Foo
{
public void Bar()
{
}
}
}
doing foreach (Foo foo in data)
is equivalent to calling
IEnumerator enumerator = ((IEnumerable)data).GetEnumerator();
Foo foo; //declared here in C# 4 and older
while(enumerator.MoveNext())
{
//Foo foo; //declared here in C# 5 and newer
foo = (Foo)enumerator.Current; //Here is the run time error in your code.
//The code inside the foreach loop.
{
foo.Bar();
}
}
So you see it does not care what type you passed in, as long as the foo = (Foo)enumerator.Current;
call succeeds.
The reason it does not throw any compile time errors is IEnumerable<T>
is covariant. That means I am allowed to pass any class that is based on Foo
or more derived from Foo
. So if I could potentially make a 2nd class that inherits from Foo
that would also support IEnumerable<Foo>
and have my list contain that instead it would cause the cast to fail.
//This code compiles fine in .NET 4.5 and runs without throwing any errors.
internal class Program
{
public static void Main()
{
var item = new Baz();
var inner = new List<Baz>();
inner.Add(item);
IEnumerable<IEnumerable<Foo>> data = inner;
foreach (Foo foo in data)
{
foo.Bar();
}
}
}
public class Foo
{
public void Bar()
{
}
}
public class Baz : Foo, IEnumerable<Foo>
{
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator<Foo> IEnumerable<Foo>.GetEnumerator()
{
throw new NotImplementedException();
}
}
However if you mark Foo
as sealed
the compiler now knows that no more derived classes could exist and then will throw the compiler error
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With