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;
}
}
Related: https://stackoverflow.com/a/32123354/259808
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");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With