Return derived type from base class method

I have a class hierarchy that looks something like 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 the fluent-api style syntax and return a derived instance, as 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; } } 
  • Why can't the compiler specify type arguments in the first example?
  • Why does it work when called using the extension method?
  • Is there a way to make it work as an instance method in a base class without explicitly specifying a type argument?

Related: stack overflow

+5
source share
1 answer

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

Type inference uses method arguments to output type arguments. In the first example, there are no method arguments that can be used to output a type argument.

Why does it work when called using the extension method?

The extension method is actually a static method, and the object that you "extend" is passed as an argument to the extension method:

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

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

Is there a way to make it work as a class-based instance method without explicitly specifying a type argument?

Make the base class general and parameterize it with the derived class (called a curiously repeating 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> { } 

Using:

 Derived d = new Derived().WithAttributes("one", "two").WithAttributes("three"); 
+11
source

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


All Articles