Bug in C# run-time type information? 
Author Message
 Bug in C# run-time type information?

The C# reference says that the fully qualified name
of a type uniquely identifies it amongst all others.
But when two classes defined in two different assemblies
have the same fully qualified name, do their instances have the
same type?

Consider the following situation:
- A common base class "Common.Base" that is extended in
 two different assemblies ("Assembly1.Derived1" and
 "Assembly2.Derived2").

- A third assembly is used as test program. It creates
  an instance of Derived1 and Derived2 and tests if the
  instance of Derived2 can be treated as an instance of
  Common.Base in the assembly containing Derived1.

The result depends on wether the instance of Derived2
was passed as a Common.Base argument or as an object.
You can reproduce it with the source code below.

It is important that you make 2 different
solutions, e.g. like this

 - solution1, containing 2 library projects:
         assembly1.dll: Base.cs  + Derived1.cs
         assembly2.dll: Base.cs  + Derived2.cs

 - solution2, containing 1 console app project:
         test.exe: reference to assembly1.dll and
                      assembly2.dll + AssemblyTest.cs

(Throwing everything in the same project/solution will
 not reproduce it...)

Output of test program:

         Assembly2.Derived2 is a Common.Base in Assembly1.Derived1
         Assembly2.Derived2 is not a Common.Base in Assembly1.Derived1

Maybe someone can explain this behaviour?

Jan

------- %< -------- %< -------- %< -------- %< --------

//Base.cs
namespace Common
{
 public class Base
 {
  public void Test1( Base arg )
  {
   System.Console.WriteLine(arg.GetType() + (arg is Common.Base ? " is" : "
is not") + " a Common.Base in " + GetType());
  }

  public void Test2( object arg )
  {
   System.Console.WriteLine(arg.GetType() + (arg is Common.Base ? " is" : "
is not") + " a Common.Base in " + GetType());
  }
 }

Quote:
}

//Derived1.cs
namespace Assembly1
{
 public class Derived1: Common.Base
 {
 }

Quote:
}

//Derived2.cs
namespace Assembly2
{
 public class Derived2: Common.Base
 {
 }

Quote:
}

// AssemblyTest.cs
public class AssemblyTest
{
 static void Main(string[] args)
 {
  Assembly1.Derived1  d1 = new Assembly1.Derived1();
  Assembly2.Derived2  d2 = new Assembly2.Derived2();

  d1.Test1(d2);
  d1.Test2(d2);
 }

Quote:
}



Sun, 03 Oct 2004 22:30:10 GMT  
 Bug in C# run-time type information?
Jan,

Quote:
>The C# reference says that the fully qualified name
>of a type uniquely identifies it amongst all others.
>But when two classes defined in two different assemblies
>have the same fully qualified name, do their instances have the
>same type?

No. The type identity is a combination of the assembly identity and
the full type name.

Mattias

===
Mattias Sj?gren (VB MVP)

http://www.msjogren.net/dotnet/



Sun, 03 Oct 2004 23:00:31 GMT  
 Bug in C# run-time type information?


Quote:
> Jan,

> >The C# reference says that the fully qualified name
> >of a type uniquely identifies it amongst all others.
> >But when two classes defined in two different assemblies
> >have the same fully qualified name, do their instances have the
> >same type?

> No. The type identity is a combination of the assembly identity and
> the full type name.

As proven by my example code this is not always the "complete truth".
Can you explain its behaviour?

Thanks,

Jan

Quote:

> Mattias

> ===
> Mattias Sj?gren (VB MVP)

> http://www.msjogren.net/dotnet/



Sun, 03 Oct 2004 23:05:24 GMT  
 Bug in C# run-time type information?
Jan,

Quote:
>As proven by my example code this is not always the "complete truth".

It is, from the runtime's perspective.

Quote:
>Can you explain its behaviour?

I'll try :-) The weird results you see is caused by what I guess you
can call a bug in the C# compiler. It apparently doesn't properly
differentiate between types with the same name in different
assemblies.

First of all, I got the impression that you were using VS.NET. If you
are, use the command line compiler instead here. It makes it easier to
see what's going on.

When you compile assembly1 and assembly2, you should get a warning
CS0183 telling you that arg in Test1 will always be Common.Base, and
therefore using the as operator on it doesn't make much sense.

This is an important warning. If you look at the generated code in
ILDASM you'll see that an actual type test (the isinst instruction)
never happens. The compiler considered it unnecessary, and instead it
just tests if the argument is null or not. This is why the first line
of the output will always print "is a", unless you pass in null.

When you compile the test app, the output you get will depend on the
order in which you reference the assemblies. If you do

csc /r:assembly1.dll,assembly2.dll /out:test1.exe AssemblyTest.cs

The output is

Assembly2.Derived2 is a Common.Base in Assembly1.Derived1
Assembly2.Derived2 is not a Common.Base in Assembly1.Derived1

But if you reverse the order to

csc /r:assembly2.dll,assembly1.dll /out:test2.exe AssemblyTest.cs

you get

Assembly2.Derived2 is a Common.Base in Assembly1.Derived1
Assembly2.Derived2 is a Common.Base in Assembly1.Derived1

It turns out that the methods called in AssemblyTest.Main will be to
the first assembly referenced. In more ILAsm style syntax, you're
calling

[assembly1]Common.Base::Test1(class [assembly1]Common.Base)
[assembly1]Common.Base::Test2(object)

in the first case, and

[assembly2]Common.Base::Test1(class [assembly2]Common.Base)
[assembly2]Common.Base::Test2(object)

in the second.

So the second line of the output is actually correct. This is because,
if you look at the ILAsm code for Test2, it actually performs an
isinst check.

The error the C# compiler does is that it even allows you to call
Test1/Test2 in assembly1 and pass in a Base reference from assembly2.
It should catch this at compile time, but instead it produces
incorrect code. Try running PEVerify on the test executable and see it
complain.

Mattias

===
Mattias Sj?gren (VB MVP)



Mon, 04 Oct 2004 00:30:54 GMT  
 Bug in C# run-time type information?


[...]

Quote:

> The error the C# compiler does is that it even allows you to call
> Test1/Test2 in assembly1 and pass in a Base reference from assembly2.
> It should catch this at compile time, but instead it produces
> incorrect code. Try running PEVerify on the test executable and see it
> complain.

Makes perfectly sense to me now!
Thanks a lot for the explanation,

Jan



Mon, 04 Oct 2004 01:16:15 GMT  
 
 [ 5 post ] 

 Relevant Pages 

1. Run-time type information

2. Enabling Run-Time Type Information: how?

3. Run-Time Type Information (RTTI) among dlls

4. Templates and MFC run-time type information

5. Enable Run-Time Type Information

6. Determining type at run-time or compile-time

7. No type information at compile time ?

8. RunTime Type Information

9. Catching run-time debug information

10. Bug in MFC 7.x: Runtime Class Information Corrupted

11. C++ runtime type information

12. run time type checking

 

 
Powered by phpBB® Forum Software