Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return derived type from base class method

Tags:

c#

generics

I have a class hierarchy that looks similar to this:

public class Base
{
    private List<string> attributes = new List<string>();

    public T WithAttributes<T>(params string[] attributes)
        where T : Base
    {
        this.attributes.AddRange(attributes);
        return this as T;
    }
}

public class Derived : Base
{
}

I want to call Base.WithAttributes from derived classes in a fluent-api style syntax and return the derived instance like shown in the example below.

void Main()
{
    Derived d = new Derived();

    // CS0411 The type arguments for method 'UserQuery.Base.WithAttributes<T>(params string[])' cannot be inferred from the usage.
    d.WithAttributes("one", "two"); 

    // Works, but type arguments must be explicity specified. 
    d.WithAttributes<Derived>("one", "two");

    // Works without explicitly specifying, but no access to private members!
    d.WithAttributesEx("one", "two");
}

public static class Extensions
{
    public static T WithAttributesEx<T>(this T obj, params string[] attributes)
        where T : Base
    {
        // No access to private members, argh!
        // obj.attributes.AddRange(attributes);
        return obj as T;
    }
}
  1. Why can't the compiler infer type arguments in the first example?
  2. Why does it work when called using an extension method?
  3. Is there any way to make it work as an instance method on the base class without explicitly specifying type argument?

Related: https://stackoverflow.com/a/32123354/259808

like image 322
tbridge Avatar asked Apr 06 '17 21:04

tbridge


1 Answers

Why can't the compiler infer type arguments in the first example?

Type inference uses method arguments to infer type arguments. In the first example there are no method arguments which can be used to infer type argument.

Why does it work when called using an extension method?

Extension method is actually a static method and object which you are 'extending' is passed as an argument to extension method call:

Extensions.WithAttributesEx<T>(d, "one", "two")

As stated above, type inference uses method arguments to find type arguments. Here type argument can be inferred from the type of first method argument, which is Derived.

Is there any way to make it work as an instance method on the base class without explicitly specifying type argument?

Make base class generic and parametrize it with derived class (that is called Curiously Recurring Template Pattern):

public class Base<T>
    where T : Base<T>
{
    private List<string> attributes = new List<string>();

    public T WithAttributes(params string[] attributes)            
    {
        this.attributes.AddRange(attributes);
        return this as T;
    }
}

public class Derived : Base<Derived>
{
}

Usage:

Derived d = new Derived().WithAttributes("one", "two").WithAttributes("three");
like image 95
Sergey Berezovskiy Avatar answered Oct 17 '22 07:10

Sergey Berezovskiy