Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

If the generic type argument (of either a calling class or calling method) is constrained with where T : Base the new method in T == Derived is not called, instead the method in Base is called.

Why is the type T ignored for method call even though it should be known before run time?

Update: BUT, when the constraint is using an interface like where T : IBase the method in Base class is called (not the method in interface, which is also impossible).
So that means the system actually is able to detect the types that far and go beyond the type constraint! Then why doesn't it go beyond the type constraint in case of class-typed constraint?
Does that mean that the method in Base class that implements the interface has 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);
    }
}

Output:

0
0
0
0
1
1
1
2
like image 969
Roland Pihlakas Avatar asked May 24 '12 15:05

Roland Pihlakas


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

What is C in C language?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.


2 Answers

Except when using dynamic objects, C# always binds methods at compile time--even when using generics. Virtual method calls are bound to virtual method slots rather than to the implementing methods, so that when they are performed on derived-class objects they will be directed to the derived-class implementations; although the methods to which the slots point will be determined at run time, the binding to the slots occurs at compile time. If a derived-class method is declared new rather than override, code which is bound using the derived class will use the derived-class method, but code which is bound using the base class will use the base-class method.

To understand why this has to be the case, imagine if it weren't. What should happen if class Base declares a method int Foo(), and a class Derived:Base declares a new string Foo(). If a generic class with constraint T:Base tries to call method Foo on an object of type T, what should the return type of that method be?

like image 58
supercat Avatar answered Sep 24 '22 20:09

supercat


It is because T is constrained to have the semantics of Base. I can't tell you exactly what is going on with the type binding at runtime, but this is my educated guess.

You are not properly overriding the method, but instead hiding via "new", if you use a reference to the base class you bypass any hiding. This is where hiding falls down.

Members that hide other members are only honoured if you are using a reference to the type in which they are hidden. You can always bypass a hidden member by using a reference to the base class:

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

To properly override a method and have this code work, mark the method as virtual in the base class and override it in the derived class.

class Base
{
    public virtual void Method() {}
}

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

You can prove this, change your generic constraint to be where T : Derived and it should hit the "new" member.

like image 35
Adam Houldsworth Avatar answered Sep 23 '22 20:09

Adam Houldsworth