How does the CLR deal with similar virual method signatures? RRS feed

  • Question

  • Hy,

    I'm studying method signatures & virtual method overriding...

    It seams that different method declaration orders produce different results !!!!

    The following example demonstrates the problem.

    IN ASSEMBLY A : We have two virtual methods declared Do(string) and Do(T)

    public class ClassA<T> where T : class  
        public virtual void Do(T s) 
        internal virtual void Do(string s) 
        public void Call() 
            Do((string)null);   // Call the method Do(string) 
            Do((T)null);        // Call the method Do(T) 

    The method ClassA.Do(string) is internal ==> Not accessible from ASSEMBLY B ==> Not overridable from ASSEMBLY B
    The method ClassA.Do(T) is public ==> Accessible and overridable from ASSEMBLY B

    public class ClassB : ClassA<string
        public override void Do(string s) 
            Console.WriteLine("Derived Do"); 

    The method ClassB.Do(string) should override the method ClassA.Do(T).

    MAIN : // Just call the the two virtual methods (ClassA.Do(String) and ClassA.Do(T))

    static void Main() 
        new B().Call(); 

    RESULT 1 :  All works fine :
    Derived Do

    TEST 2 : reverse the declaration order of the methods Do(String) and Do(T)

    RESULT 2 : 

    I got a System.TypeLoadException: Method 'Do' on type 'ClassB' from assembly 'B, Version=, Culture=neutral, PublicKeyToken=null' is overriding a method that is not visible in this assembly.

    It seams that the CLR only takes into account only the last method emited !
    Thursday, December 4, 2008 12:56 AM

All replies

  • Oddly, the compiler will not compile all this code if it is placed in the same assembly.  You get a warning and an error:

    Warning 1 Member 'ConsoleApplication1.Program.ClassB.Do(string)' overrides 'ConsoleApplication1.Program.ClassA<string>.Do(string)'. There are multiple override candidates at run-time. It is implementation dependent which method will be called. \Temporary Projects\ConsoleApplication1\Program.cs 18 33 ConsoleApplication1

    Error 2 The inherited members 'ConsoleApplication1.Program.ClassA<T>.Do(T)' and 'ConsoleApplication1.Program.ClassA<T>.Do(string)' have the same signature in type 'ConsoleApplication1.Program.ClassB', so they cannot be overridden \Temporary Projects\ConsoleApplication1\Program.cs 37 34 ConsoleApplication1

    I think that the "implementation dependent" wording is very telling that you should not expect this to work correctly.
    Thursday, December 4, 2008 1:55 AM
  • When you instantiate ClassA<T> with T = string you effectively get two methods with identical signatures: void Do(String).  Not a big surprise that there's trouble with overload resolution.  I'm more surprised that this code ever compiles at all -- IMO the C# compiler should stop immediately upon reaching the ClassA<String> instantiation in assembly B, and flag the duplicate method signature.

    Thursday, December 4, 2008 8:33 AM
  • It's worse when we use it at MSIL level because the ILASM doesn't show any error or warning. Even within the same assembly such problems can occur. In some cases, even PEVerify doesn't show any warrning or error. Here is another example in MSIL :

    The two following methods have different signatures and can both
    coexist in the same class ClassA

    class public ClassA 
      .method public newslot virtual instance void VMethod(class ClassA i) 
    cil managed {...} 
      .method public newslot virtual instance void VMethod(class 
    [MyAssembly]ClassA i) cil managed {...} 
    // Now if we define two classes B and C that both extend the class A and 
    // redefines both methods in different orders
    class public ClassB extends ClassA 
      .method public virtual instance void VMethod(class ClassA i) cil 
    managed {...} 
      .method public virtual instance void VMethod(class 
    [MyAssembly]ClassA i) cil managed {...} 
    class public ClassC extends ClassA 
      .method public virtual instance void VMethod(class 
    [MyAssembly]ClassA i) cil managed {...} 
      .method public virtual instance void VMethod(class ClassA i) cil 
    managed {...} 

    Note that the classes B and C differ just in the order of method
    declaration !

    Calling one of the virtual methods of ClassA with an instance of the
    class B will always call the
    "instance void VMethod(class [MyAssembly]ClassA)". But calling them
    with an instance of the class C will always call the "instance void
    VMethod(class ClassA)"

    In all cases, it seems that the CLR takes into account the last emited method.
    Thursday, December 4, 2008 10:22 AM