Curiously repeating pattern templates with additional generic types

Let's say I want to have a generic Box class that can contain something inside, so it's Box<T> . Box<T> has a Transform method that returns Box<U> :

 public Box<U> Transform<U>(Func<T, U> transform) 

So far it has been pretty simple. However, I really need an abstract Box , since the way to insert and convert values ​​is implementation dependent. (I cannot have an interface, since there are other methods that are implemented using the composition of abstract methods, but this does not change anything).

Of course, I want my overridden Transform methods to return the corresponding subclass of Box , not Box . Since the return types of overriding methods are invariant in C #, I turn to a curiously repeating template pattern (see IComparable<T> ):

 public abstract class Box<B, T> where B : Box<B, T> 

Now, every class that I inherit from Box<B, T> must refer to itself or the whole hell breaks:

 public class FooBox<T> : Box<FooBox, T> 

However, this completely destroys the Transform method:

 public abstract Box<B, U> Transform<U>(Func<T, U> transform); 

The type 'B' cannot be used as type parameter 'B' in the generic type or method 'Test.Box<B,T>'. There is no implicit reference conversion from 'B' to 'Test.Box<B,U>'. (CS0311) The type 'B' cannot be used as type parameter 'B' in the generic type or method 'Test.Box<B,T>'. There is no implicit reference conversion from 'B' to 'Test.Box<B,U>'. (CS0311) The type 'B' cannot be used as type parameter 'B' in the generic type or method 'Test.Box<B,T>'. There is no implicit reference conversion from 'B' to 'Test.Box<B,U>'. (CS0311) . This makes sense since the return type is now Box<B, U> , and B is Box<B, T> , which is not Box<B, U> .

A simple fix will not work:

 public abstract Box<B, U> Transform<U>(Func<T, U> transform) where B : Box<B, U>; 

cannot compile with 'Test.Box<B,T>.Transform<U>()' does not define type parameter 'B' (CS0699) .

Is there any way to resolve this, or have I really painted myself in a corner?

+6
source share
1 answer

I think the direct fix problem is reusing a type B parameter. Try something else and include it as a type parameter:

 public abstract Box<B2, U> Transform<B2,U>(Func<T, U> transform) where B2 : Box<B2, U>; 

Update: you stated:

now I can’t guarantee that B2 and B are actually the same derived class that was my goal

This is not so, but B2 does not inherit from B , U cannot (cannot?) Inherit T , if you like. You can include this as a limitation. This is not strictly necessary for the template, though, since it is sorted before the Transform body.

eg:

 public abstract Box<B2, U> Transform<B2,U>(Func<T, U> transform) where B2 : Box<B2, U> where U : T 
+1
source

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


All Articles