Type inference for a method using generics and class inheritance

I have a class hierarchy that looks like this:

class Base<TElement> { public TElement Element { get; set; } } class Concrete : Base<string> { } 

I would like to write a method that accepts subclasses of Base :

 public TConcrete DoSomething<TConcrete, TElement>() where TConcrete : Base<TElement> { } 

Is there a way to define DoSomething without defining a TElement ?

An ideal solution would be if the compiler could digit TElement automatically, so the call code would look like this:

 var item = DoSomething<Concrete>(); 

I am using C # 4.0.

+4
source share
3 answers

This is not possible for the following reasons:

  • Starting with C # 4, all-or-nothing output - the compiler cannot output some common arguments, but not others.
  • With C # 4, it is not possible to specify common "wildcards", such as where TConcrete : Base<???> .

Here are some workarounds.

Not a common type of base . Create a base class or interface type that is not shared. This is the big picture; e.g. IEnumerable<T> : IEnumerable .


Covariant interface . With covariance of common C # 4 code, you can create a type-safe solution that does not require cluttering your types with ugly non-native members:

 public interface IBase<out TElement> { TElement Element { get; } } class Base<TElement> : IBase<TElement> { public TElement Element { get; set; } } class Concrete : Base<string> { } 

And then:

 // Won't work with value types. public TConcrete DoSomething<TConcrete>() where TConcrete : IBase<object> { } 

And call it that:

 var item = DoSomething<Concrete>(); 
+4
source

If you make Base inherit a non-generic class or implement a non-generic interface, you can restrict this method to this type.

Otherwise, no. If it were possible, the TConcrete.Element property inside your method would not be a type.
What happens if you write

 public TConcrete DoSomething<TConcrete>() where TConcrete : Base<> //Illegal! { TConcrete c = ...; var b = c.Element; //What type is that variable? } 
+1
source

If DoSomething doesn't know (or doesn't care) about what TElement , you might need to examine the parent class without the type parameter:

 class Base { } class Base<TElement> : Base { public TElement Element { get; set; } } 

Then your DoSomething method will work with the class base.

If DoSomething needs to know the type parameter, then no, there is no way to do what you want, and you need to provide it.

0
source

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


All Articles