Curiously recurring template patterns with additional generic types
比如说,我想要一个通用的Box类,它可以包含一些内部内容,所以它是Box类。Box有一个Transform方法,该方法返回Box:
1
| public Box<U> Transform<U>(Func<T, U> transform) |
到目前为止,这很简单。但是,我实际上需要一个抽象的Box,因为值的装箱和转换方式是特定于实现的。(我不能有接口,因为有其他方法是通过抽象方法的组合来实现的,但这无论如何都不会改变任何东西)。
当然,我希望重写的Transform方法返回Box的适当子类,而不是Box本身。由于重写方法的返回类型在C中是不变的,所以我使用奇怪的循环模板模式(参见IComparable)。
1
| public abstract class Box<B, T> where B : Box<B, T> |
现在,我从以东所继承的每一个阶级(10)都应该指代它自己,否则所有的地狱都会散去:
1
| public class FooBox<T> : Box<FooBox, T> |
但是,这完全破坏了Transform方法:
1
| 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'. There is no implicit reference conversion from 'B' to 'Test.Box'. (CS0311)编译。这是有道理的,因为返回类型现在是Box,而b是Box,而不是Box。
直截了当的解决方案不起作用:
1
| public abstract Box<B, U> Transform<U>(Func<T, U> transform) where B : Box<B, U>; |
无法用'Test.Box.Transform()' does not define type parameter 'B' (CS0699)编译。
有没有办法解决这个问题,或者我真的把自己画成了一个角落?
- 你能把它变成一个Box并通过工厂方法传递实现特定的信息吗?
- @当然,去除crtp的Milliesmith可以工作,但这将导致无法控制像Box Merge(Box another, Func merge)这样的方法实际上接受兼容的Boxes。
我认为直接修复的问题是重用B类型参数。尝试其他方法,并将其包含为类型参数:
1
| public abstract Box<B2, U> Transform<B2,U>(Func<T, U> transform) where B2 : Box<B2, U>; |
更新:您声明:
now I cannot guarantee that B2 and B are actually the same derived class, which was my goal
但事实并非如此,B2没有(不能?)从B继承,如果你愿意,U可以继承T。您可以将其作为约束包含在内。不过,这对于模式来说并不是绝对必要的,因为它取决于Transform的主体来解决问题。
例如:
1 2 3
| public abstract Box<B2, U> Transform<B2,U>(Func<T, U> transform)
where B2 : Box<B2, U>
where U : T |
- 不是100%你需要B2而不是B,但你肯定需要它作为Transform的类型参数。
- 它可以编译(如果我将b2添加到方法的泛型参数中),但现在我不能保证b2和b实际上是相同的派生类,这是我的目标。
- 更新答案。
- 我实际上想确保b2和b是相同的类,u和t是完全独立的类型。假设InfiniteSequence是派生类之一,我想从转换中得到一个无限序列,而不是一个抽象框。
- 我想你想说的是,虽然b和b2不是同一类型,但它们应该是同一个实例,但两者之间没有任何继承关系?我想你不会明白的。想想克隆盒和铸造T->U的过程也许。
- 不,我正试图设置通用约束,以允许我使用以下签名FooBox Transform(Func transform)重写FooBox : Box中的方法,而不是BarBox Transform(Func transform)或Box的任何其他子代。