是否可以在C#中为泛型制作“此类型”?

时间:2009-09-09 16:54:42

标签: c# .net generics

有点理论问题。很长一段时间,如果你不喜欢理论,可以随意跳过。

想象一下,你有两个类,一个继承自另一个。基类是通用的,并且有一个方法,在闭合类型中必须返回此闭合类型的某个实例。

像这样(注意文字中的 ??? ):

public class Adapter<T>
{
 public virtual ??? DoSomething()
 {
  ...
 }
}

public class AdaptedString : Adapter<String>
{
 public override AdaptedString DoSomething()
 {
  ...
 }
}

我不能这样做,因为无法引用将从泛型类型派生的闭合类型。 (对不起破解语言,只是不知道如何表达它。)没有设置关键字代替???来指定此方法将返回将从此泛型类型派生的类型的实例。

相反,我可以使用显式将类型名称传递给通用基础的变通方法。但它看起来多余。

public class Adapter<TThis,T>
{
 public virtual TThis DoSomething()
 {
  ...
 }
}

public class AdaptedString : Adapter<AdaptedString,String>
{
 public override AdaptedString DoSomething()
 {
  ...
 }
}

如果在基类中我需要访问TThis实例的成员,我必须添加一个约束。这一次看起来很难看 - 请注意约束:

public class Adapter<TThis,T>
 where TThis : Adapter<TThis, T>
{
 protected int _field; 

 ...

 public bool Compare( TThis obj )
 {
  return _field == obj._field;
 }
}

public class AdaptedString : Adapter<AdaptedString,String>
{
 ...
}

是的,它一切正常,但如果我只是在第一个代码片段中使用一些关键字而不是???,它看起来会更好。像“ thistype ”之类的东西。

您认为它如何运作?它有用吗?或者这可能只是愚蠢的?

4 个答案:

答案 0 :(得分:11)

没有任何东西可以让这种模式变得更容易,事实上这种模式无论如何都不是防弹 - 因为你可以拥有:

class TypeA : Adapter<TypeA, string>

class TypeB : Adapter<TypeA, string> // Bug!

这里的第二行完全合法 - TypeATThis类型参数的有效类型参数,即使它不是我们想要的。基本上,类型系统不允许我们表达“T必须是这种类型”的概念。

但是,我不同意那些说这是一种坏的或无用的模式的人。我发现在Protocol Buffers中它很有用(如果复杂的话) - 如果没有它会更糟糕的很多。例如:

Foo foo = new Foo.Builder { Name="Jon" }.Build();
即使在Foo.Build()中指定了Foo方法,如果Build未被强类型返回IBuilder<...>

也无效。

如果你很容易可以因为它变得如此复杂 - 值得避免这种情况 - 但我确实认为这是一个有用的模式。

答案 1 :(得分:5)

在这种情况下,您通常只想引用基类:

public class Adapter<T> { 
   public virtual Adapter<T> DoSomething();

尝试执行您正在执行的操作违反了Liskov substitution principal

答案 2 :(得分:-1)

如果派生类中的继承方法需要返回派生类型而不是基类型(称为covariant return type),则C#中已经支持此功能。

答案 3 :(得分:-1)

我也很难找到一个有争议的用例(虽然这是一个有趣的想法)。

您是否试图转变如何约束您可以使用的通用类型?听起来你想在不知道实际类型的情况下假设一些基本功能;这就是接口的用途。 where子句对于这些问题非常方便。

class Dictionary<K, V>
where K : IComparable, IEnumerable
where V : IMyInterface
{
    public void Add(K key, V val)
    {
    }
}

上面的示例约束K(密钥),因此它必须具有可比性和可枚举性,并且V必须通过您自己的界面实现您想要的任何客户功能。