Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the following Code Compiling and Executing successfully?

I compiled the following code in .Net 3.5, Visual Studio 2012.

I expected to get an error on the line when the array gets assigned to my IReadOnlyCollection, because there is no implicit conversion defined from Array to my Interface. It compiles successful and does also not create any runtime errors.

Notes to be aware of:

  • There is no other IReadonlyCollection referenced. So it has to use mine (IReadonlyCollection was added to .Net4.5 and does not exist in earlier versions)
  • When i rename it to IMyCollection it does not compile anymore
  • When i change the namespace it does not compile anymore.

File1.cs:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Collections.Generic
{
    public interface IReadOnlyCollection<T> : IEnumerable<T>, IEnumerable
    {
        int Count
        {
            get;
        }
    }
}

File2.cs:

using System.Collections.Generic;

namespace ConsoleApplication1
{
    public class Test
    {
        public Test()
        { }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Test[] foo = { new Test(), new Test(), new Test() };


            IReadOnlyCollection<Test> bar = foo;

            int count = bar.Count;
        }
    }
}

This is the IL code by the way:

   .method private hidebysig static void Main (
            string[] args
        ) cil managed 
    {
        .entrypoint
        .locals init (
            [0] class ConsoleApplication1.Test[] foo,
            [1] class System.Collections.Generic.IReadOnlyCollection`1<class ConsoleApplication1.Test> bar,
            [2] int32 count,
            [3] class ConsoleApplication1.Test[] CS$0$0000
        )

        IL_0000: nop
        IL_0001: ldc.i4.3
        IL_0002: newarr ConsoleApplication1.Test
        IL_0007: stloc.3
        IL_0008: ldloc.3
        IL_0009: ldc.i4.0
        IL_000a: newobj instance void ConsoleApplication1.Test::.ctor()
        IL_000f: stelem.ref
        IL_0010: ldloc.3
        IL_0011: ldc.i4.1
        IL_0012: newobj instance void ConsoleApplication1.Test::.ctor()
        IL_0017: stelem.ref
        IL_0018: ldloc.3
        IL_0019: ldc.i4.2
        IL_001a: newobj instance void ConsoleApplication1.Test::.ctor()
        IL_001f: stelem.ref
        IL_0020: ldloc.3
        IL_0021: stloc.0
        IL_0022: ldloc.0
        IL_0023: stloc.1
        IL_0024: ldloc.1
        IL_0025: callvirt instance int32 class System.Collections.Generic.IReadOnlyCollection`1<class ConsoleApplication1.Test>::get_Count()
        IL_002a: stloc.2
        IL_002b: ret
    }
like image 894
rudimenter Avatar asked Mar 13 '14 12:03

rudimenter


1 Answers

All I'm saying is pure guess, but it doesn't fit as a comment so I'm posting it as an answer anyway:

  • I can reproduce your issue with VS 2012 and .Net 3.5.
  • I can't reproduce it with VS 2010, .Net 3.5 and identical code.

So the difference is really the compiler version.

As the name of the class and the namespace are important, I'm assuming it's a hardcoded rule that has been introduced in the compiler of VS 2012+, to support implicit conversion of the new types/interfaces introduced with .Net 4.5.

So I'm guessing it's another black magic trick with arrays. See for example this Hans Passant answer:

Both the compiler and the CLR have special knowledge of array types, just as they do of value types. The compiler sees your attempt at casting to IList<> and says "okay, I know how to do that!".

like image 53
ken2k Avatar answered Oct 23 '22 17:10

ken2k