【问题标题】:Curiously recurring template patterns with additional generic types带有其他泛型类型的奇怪重复模板模式
【发布时间】:2015-04-23 15:15:56
【问题描述】:

假设我想要一个通用的Box 类,它可以在里面包含一些东西,所以它是一个Box<T>Box<T> 有一个 Transform 方法,它返回一个 Box<U>

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

到目前为止,这非常简单。但是,我实际上需要一个抽象的Box,因为值的装箱和转换方式是特定于实现的。 (我不能有接口,因为还有其他方法是通过抽象方法的组合来实现的,但这并没有改变任何东西)。

当然,我希望我重写的Transform 方法返回Box 的适当子类,而不是Box 本身。由于覆盖方法的返回类型在 C# 中是不变的,所以我转向 curiously recurring template pattern(参见 IComparable&lt;T&gt;):

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

现在我从 Box&lt;B, T&gt; 继承的每个类都应该引用它自己,否则一切都会崩溃:

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

但是,这完全破坏了Transform 方法:

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&lt;B,T&gt;'. There is no implicit reference conversion from 'B' to 'Test.Box&lt;B,U&gt;'. (CS0311) 编译失败。这是有道理的,因为返回类型现在是 Box&lt;B, U&gt; 而 B 是 Box&lt;B, T&gt;,而不是 Box&lt;B, U&gt;

简单的修复方法不起作用:

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

使用'Test.Box&lt;B,T&gt;.Transform&lt;U&gt;()' does not define type parameter 'B' (CS0699) 编译失败。

有没有办法解决这个问题,还是我真的把自己画到了角落里?

【问题讨论】:

  • 你能把它设为Box&lt;U&gt; 并通过工厂方法传递实现特定的信息吗?
  • @MillieSmith 删除 CRTP 当然可以,但这会导致无法控制像 Box&lt;B, V&gt; Merge&lt;U, V&gt;(Box&lt;B, U&gt; another, Func&lt;T, U, V&gt; merge) 这样的方法实际上接受兼容的 Boxes。

标签: c# generics crtp


【解决方案1】:

我认为直接修复的问题是B 类型参数的重用。尝试其他方法,并将其作为类型参数包含在内:

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

更新:你说:

现在我不能保证 B2 和 B 实际上是同一个派生类,这是我的目标

但情况并非如此,B2 不会(不能?)从 B 继承,U 可能会继承 T,如果你愿意的话。您可以将其作为约束包括在内。不过,这对于该模式并不是绝对必要的,因为它由 Transform 的主体来解决。

例如:

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 不是同一种类型,但它们应该是同一个实例,但两者之间没有任何继承关系对吧?我不认为你会得到那个。考虑克隆 Box 并在此过程中投射 T -> U。
  • 不,我正在尝试设置通用约束,允许我使用以下签名 FooBox&lt;U&gt; Transform&lt;U&gt;(Func&lt;T, U&gt; transform) 覆盖 FooBox&lt;T&gt; : Box&lt;FooBox, T&gt; 中的方法,但不能使用 BarBox&lt;U&gt; Transform&lt;U&gt;(Func&lt;T, U&gt; transform) 或 @987654336 的任何其他子项@.
猜你喜欢
  • 2011-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-21
  • 2019-06-03
  • 1970-01-01
相关资源
最近更新 更多