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?
source to share
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();
}
source to share
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
source to share
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
.
source to share