C # & generics - why is a method in a base class called instead of a new method in a derived class?

If the argument of the general type (both the calling class and the invocation method) is limited to where T : Base , the new method in T == Derived is not called, but the method is called in Base.

Why is type T ignored to call the method, although it must be known before runtime?

Update : BUT, when the restriction uses an interface like where T : IBase , the method in the base class is called (and not the method in the interface, which is also impossible).
Thus, this means that the system can indeed detect types that are far and go beyond the type limit! Then why doesn’t it go beyond the bounds of type restriction in case of class type restriction?
Does this mean that a method in the Base class that implements the interface has an implicit override keyword for the method?

Test code:

 public interface IBase { void Method(); } public class Base : IBase { public void Method() { } } public class Derived : Base { public int i = 0; public new void Method() { i++; } } public class Generic<T> where T : Base { public void CallMethod(T obj) { obj.Method(); //calls Base.Method() } public void CallMethod2<T2>(T2 obj) where T2 : T { obj.Method(); //calls Base.Method() } } public class GenericWithInterfaceConstraint<T> where T : IBase { public void CallMethod(T obj) { obj.Method(); //calls Base.Method() } public void CallMethod2<T2>(T2 obj) where T2 : T { obj.Method(); //calls Base.Method() } } public class NonGeneric { public void CallMethod(Derived obj) { obj.Method(); //calls Derived.Method() } public void CallMethod2<T>(T obj) where T : Base { obj.Method(); //calls Base.Method() } public void CallMethod3<T>(T obj) where T : IBase { obj.Method(); //calls Base.Method() } } public class NewMethod { unsafe static void Main(string[] args) { Generic<Derived> genericObj = new Generic<Derived>(); GenericWithInterfaceConstraint<Derived> genericObj2 = new GenericWithInterfaceConstraint<Derived>(); NonGeneric nonGenericObj = new NonGeneric(); Derived obj = new Derived(); genericObj.CallMethod(obj); //calls Base.Method() Console.WriteLine(obj.i); genericObj.CallMethod2(obj); //calls Base.Method() Console.WriteLine(obj.i); genericObj2.CallMethod(obj); //calls Base.Method() Console.WriteLine(obj.i); genericObj2.CallMethod2(obj); //calls Base.Method() Console.WriteLine(obj.i); nonGenericObj.CallMethod(obj); //calls Derived.Method() Console.WriteLine(obj.i); nonGenericObj.CallMethod2(obj); //calls Base.Method() Console.WriteLine(obj.i); nonGenericObj.CallMethod3(obj); //calls Base.Method() Console.WriteLine(obj.i); obj.Method(); //calls Derived.Method() Console.WriteLine(obj.i); } } 

Conclusion:

 0 0 0 0 1 1 1 2 
+6
source share
4 answers

dynamic objects are used, C # always binds methods at compile time - even when using generics. Calls of virtual methods are tied to slots of virtual methods, and not to implementation methods, therefore, when they are performed on objects of a derived class, they will be directed to the implementation of the derived class; although methods for which the point of the slots will be determined at run time, binding to the slots occurs at compile time. If the method of the derived class is declared new and not override , the code associated with the derived class will use the method of the derived class, but the code associated with using the base class will use the base class method.

To understand why this should be, consider whether it was. What happens if the Base class declares an int Foo() method, and the Derived:Base class declares a new string Foo() . If a generic class with a T:Base constraint tries to call the Foo method on an object of type T , what should the return type of this method be?

+7
source

This is because T limited by Base semantics. I cannot say exactly what happens with type binding at runtime, but this is my reasonable assumption.

You do not override the method properly, but instead hide it with "new", if you use a reference to the base class, you bypass any hiding. Here is a recession.

Members that hide other members are executed only if you use a link to the type in which they are hidden. You can always bypass the hidden element using the link to the base class:

 var derived = new Derived(); var baseRef = (Base)derived; baseRef.Method(); // calls Base.Method instead of Derived.Method. 

To correctly override a method and use this code, mark the method as virtual in the base class and override in the derived class.

 class Base { public virtual void Method() {} } class Derived : Base { public override void Method() {} } 

You can prove it, change the general constraint to where T : Derived , and it should fall into the "new" element.

+6
source

This is due to the nature of the new operator: A new difference from overriding, create a function with the same name as the base one, which masks the base method but does not cancel it.

To do this, without the correct cast, the original method will be called if the link is of type Base.

0
source

The new keyword simply hides the method, not overloads it. The reason your non-generic CallMethod works as expected is because the method signature expects Derived instead of Base .

Generics are not really the culprits. If you change the method signature to CallMethod(Base obj) , you will see the same "unexpected" behavior as the general implementation, and get the following result:

 0 0 0 0 0 0 0 1 

If you make Base.Method virtual and override it with Derived.Method as follows:

 public class Base { public virtual void Method() { } } public class Derived : Base { public int i = 0; public override void Method() { i++; } } 

You will get the following result:

 1 2 3 4 5 6 7 8 

Edit: Updated to match the updated release.

0
source

Source: https://habr.com/ru/post/916578/


All Articles