I'm trying to write a method which casts an object to a generic type to execute 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 is:
hello
hello2
I debugged it, checked everything and couldn't find a mistake. The Output should be hello2 in both cases. Am I missing something obvious here? Or is this just not working?
The thing you are missing is the method that is called is chosen at the compile time of hello
not at the use of write2
. So at compile time all the compiler knows is that T
is of type hello
or some other derived class so it chooses the shadowed method as that is the only method it knows about at compile time.
Think of it this way, replace every T
with whatever is on the right side of the :
. This is what the compiler sees and uses that information to make its choices.
//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();
}
When the method is declared as virtual
the resolution of the method that will be called is deferred until runtime and depends on the type of the object at runtime. From the C# spec :
In a virtual method invocation, the run-time type of the instance for which that invocation takes place determines the actual method implementation to invoke.
From the C# spec , the resolution of a virtual method is as follows:
For every virtual method declared in or inherited by a class, there exists a most derived implementation of the method with respect to that class. The most derived implementation of a virtual method M with respect to a class R is determined as follows:
- If R contains the introducing virtual declaration of M, then this is the most derived implementation of M.
- Otherwise, if R contains an override of M , then this is the most derived implementation of M.
- Otherwise, the most derived implementation of M with respect to R is the same as the most derived implementation of M with respect to the direct base class of R.
So when you write:
T h2 = (T)this;
h2.write();
The compiler knows h2 is of type hello
or derived, and that write
is a virtual method whose resolution will be deferred until runtime.
At runtime, the resolution will chose the method in class hello
even though the class is of type hello2
. This is because the write
method in class hello2
does not contains the override
keyword and won't be considered when resolving the virtual call. (So the only way to call write on hello2
is by making sure the compiler knows the instance is of type hello2
)
This is also why if you declare write on hello2
with override instead of new the output of the same program would be hello2 in both cases.
Edit
It seems you want to write a method that is able to call write
in hello
or hello2
even though the method has been declared with the new operator in hello2
.
You could write a extension method that uses a dynamic
variable. 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 could write the following test program that uses that 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
The write
method that 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 write
method defined on the hello
type (check it using intellisense). Therefore when you are actually calling the method with a specific T type of type hello2
it is still the write
method if type hello
. And since you are defining the write
method in type hello2
using the new operator, this method is not called.
You can only use methods of type hello2
if you explicitly cast it to type hello2
in your method , and not to not-yet-known-type 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.