C # Shadow invocation method with generic type

I am trying to write a method that casts an object on a generic type to do a specific shadow method. This is my test code:

class Program
{
    static void Main(string[] args)
    {
        hello2 h2 = new hello2();
        test(h2);
        Console.ReadLine();
    }

    static void test(hello h)
    {
        h.write2<hello2>();
    }
}

class hello
{
    public virtual void write()
    {
        Console.WriteLine("hello");
    }

    public void write2<T>() where T : hello
    {
        T h2 = (T)this;
        hello2 h21 = (hello2)this;
        h2.write();
        h21.write();
    }
}

class hello2 : hello
{
    public new void write()
    {
        Console.WriteLine("hello2");
    }
}

      

My console output:

Hello

hello2

I debugged it, checked everything and couldn't find the error. The result should be hello2 in both cases. Am I missing something obvious here? Or does it just not work?

+3


source to share


3 answers


What you are missing is the method that gets called, is fetched at compile time hello

not when used write2

. Therefore, at compile time, all compilers know that it T

has a type hello

or some other derived class, so it chooses the shadow method, since that is the only method it knows at compile time.

Think of it this way, replace each with the T

one on the right :

. This is what the compiler sees and uses this information to select.



//What the complier sees when you compile even if you pass in hello2 as T.
public void write2<T>() where T : hello
{
    hello h2 = (hello)this;
    hello2 h21 = (hello2)this;
    h2.write();
    h21.write();
}

      

+3


source


When a method is declared as virtual

, the resolution of the method to be called is deferred until runtime and depends on the type of the object at runtime. From the C # spec :

In a virtual method call, the runtime type of the instance for which the call takes place determines the actual method to call.

From the C # spec , the virtual method resolution looks like this:

For every virtual method declared or inherited by a class, there is a most derivative implementation of the method with respect to that class. The most derivative implementation of the virtual method M with respect to the class R is defined as follows:

  • If R contains the injected virtual declaration M, then it is the most derived implementation of M.
  • Otherwise, if R contains an override of M , then it is the most derived implementation of M.
  • Otherwise, the most derivative implementation of M with respect to R is the same as the most derivative implementation of M with respect to the direct base class R.

So when you write:

T h2 = (T)this;
h2.write();

      

The compiler knows that h2 is of type hello

or derived, and that it write

is a virtual method whose resolution will be deferred until execution.

At runtime, the resolution will select a method in the class hello

even though the class is of type hello2

. This is because the method write

in the class hello2

does not contain the keyword override

and will not be considered when resolving the virtual call. (Thus, the only way to trigger the entry on hello2

is if the compiler knows the instance is of type hello2

)

This is also because if you declare an entry hello2

with override instead of new , then the output of the same program will be hello2 in both cases.



Edit

You seem to want to write a method that can be called write

in, hello

or hello2

even if that method was declared a new operator in hello2

.

You can write an extension method that uses a variable dynamic

. For example, create the following extension method:

public static class HelloExtensions
{
    public static void writeDynamic<T>(this T h) where T: hello
    {
        dynamic hdynamic = h;
        hdynamic.write();
    }
}

      

Then you can write the following test program that uses this extension method with your original classes:

class Program
{
    static void Main(string[] args)
    {
        testDynamic(new hello());
        testDynamic(new hello2());
        Console.ReadLine();
    }

    static void testDynamic(hello h)
    {
        h.writeDynamic();
    }
}

      

The output will be:

hello
hello2

      

+3


source


The method write

you are using in your method

public void write2<T>() where T : hello {
    T h2 = (T)this;
    hello2 h21 = (hello2)this;
    h2.write();  // <--- this one
    h21.write();
}

      

is the method write

defined in the type hello

(test it with intellisense). So when you actually call a method with a specific type of type T hello2

, it is still a write

method if type hello

. And because you are defining a method write

on a type hello2

with a new operator, that method is not called.

You can only use type methods hello2

if you have explicitly applied it to the type hello2

in your method and not to a type that is not yet known T

.

0


source







All Articles