简体   繁体   中英

Using Activator.CreateInstance in generic class combined with “new” modifier on method

I have a class (DerivedClass) which inherits from BaseClass. In DerivedClass I use the "new" modifier on a method (SayHello()) since I want to alter the signature - I want to add a return value.
I also have a generic class. The type supplied to the generic class should be of type "BaseClass" (in my case either BaseClass or DerivedClass).

If I use Activator.CreateInstance<T>() to get a new instance of my generic type and then call my method then the method on BaseClass is always called. Why doesn't the SayHello-method on DerivedClass get called when it is set as the generic type?

I made a simple console application to illustrate:

namespace TestApp
{
    using System;

    class Program
    {
        static void Main(string[] args)
        {
            var gc = new GenericClass<DerivedClass>();
            gc.Run();
        }
    }

    public class BaseClass
    {
        public void SayHello()
        {
            Console.WriteLine("Hello!");
        }
    }

    public class DerivedClass : BaseClass
    {
        new public int SayHello()
        {
            Console.WriteLine("Hello returning int!");
            return 1;
        }
    }

    public class GenericClass<T> where T : BaseClass
    {
        public void Run()
        {
            var bc = new BaseClass();
            bc.SayHello(); // Hello!

            var dc = new DerivedClass();
            dc.SayHello(); // Hello returning int!

            var dc2 = Activator.CreateInstance<T>();
            dc2.SayHello(); // Hello!
            Console.WriteLine(dc2.GetType()); // TestApp.DerivedClass

            Console.Read();
        }
    }
}

Because you didn't override the method, you're shadowing it. If you made the method virtual, and override it instead, then you'd get the results you're expecting.

The Run method of GenericClass doesn't know the runtime type of the object, it only knows that it's a type derived from BaseClass , and so it can only bind the implementation of the method in BaseClass at compile time. Since you haven't enabled virtual dispatch through making the method virtual , there's no way for it to know about the derived type's method.

Because the combination of:

public class GenericClass<T> where T : BaseClass

And:

new public int SayHello()

Tells the compiler, that at compile time, T would be of type BaseClass , and the method overload matching happens at compile time, not at runtime. Hence, the fact that your run-time type is different, doesn't actually come into play here as it was "ran over" using the new modifier, and not via overriding of a virtual method dispatch, as it would if the return type of both your method calls was the same ( void ).

You see it in the generated IL:

GenericClass`1.Run:

IL_001B:  call        01 00 00 2B 
IL_0020:  stloc.2     // dc2
IL_0021:  ldloca.s    02 // dc2
IL_0023:  constrained. 02 00 00 1B 
IL_0029:  callvirt    UserQuery+BaseClass.SayHello

您可以使用动态关键字:

 dynamic dc2 = Activator.CreateInstance<T>(); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM