C# 接口中的类型约束应用于基类
我有一个基类,它定义了这样一个通用方法:C# 接口中的类型约束应用于基类,c#,generics,type-constraints,C#,Generics,Type Constraints,我有一个基类,它定义了这样一个通用方法: public class BaseClass { public T DoSomething<T> () { ... } } 由于BaseClass中的DoSomething已经支持任何类型参数T,因此它应该特别支持作为Foo子类型的类型参数。因此,可以预期,BaseClass的子类型已经实现了接口。但是,我得到以下错误: 方法“BaseClass.DoSomething()”的类型参数“T”的约束必须与接口方法“ISomet
public class BaseClass
{
public T DoSomething<T> ()
{ ... }
}
由于BaseClass
中的DoSomething
已经支持任何类型参数T
,因此它应该特别支持作为Foo
子类型的类型参数。因此,可以预期,BaseClass
的子类型已经实现了接口。但是,我得到以下错误:
方法“BaseClass.DoSomething()”的类型参数“T”的约束必须与接口方法“ISomething.DoSomething()”的类型参数“T”的约束匹配。考虑使用显式接口实现,而不是
现在,我有两种可能性;第一种方法是按照错误建议执行,并明确地实现接口。第二种方法是使用new
隐藏基本实现:
// Explicit implementation
T ISomething.DoSomething<T> ()
{
return base.DoSomething<T>();
}
// Method hiding
public new T DoSomething<T>()
where T : Foo
{
return base.DoSomething<T>();
}
//显式实现
例如:T something.DoSomething()
{
返回base.DoSomething();
}
//方法隐藏
公共新剂量
T:Foo在哪里
{
返回base.DoSomething();
}
虽然我可能更喜欢第二种解决方案,使方法可以从类本身访问,但这两种方法都可以。然而,它仍然留下以下问题:
当基类型已经使用不太严格的(读取:none)类型约束来实现该方法时,为什么我必须重新实现该方法?为什么该方法需要按原样实现
edit:为了使该方法更有意义,我将返回类型从
void
更改为T
。在我的实际应用程序中,我既有泛型参数又有返回值。尝试使用组合而不是继承来实现某些东西:
public class Something : ISomething
{
private readonly BaseClass inner = ...;
void DoSomething<T>() where T : Foo
{
inner.DoSomething<T>();
}
}
公共类某物:等轴物
{
私有只读基类内部=。。。;
void DoSomething(),其中T:Foo
{
DoSomething();
}
}
当然,给定的代码可以安全编译和运行:
当Something
实例被键入为Something
或BaseClass
时,编译器将允许T
的任何类型,而当同一实例被键入为ISomething
时,它将只允许继承Foo
的类型。在这两种情况下,您都可以像往常一样进行静态检查和运行时安全性
事实上,当您明确实施ISomething
时,上述场景正是发生的。那么,让我们看看我们可以提出哪些支持和反对当前事态的论点
用于:
- 拟议的解决办法不适用于所有情况;它取决于精确的方法签名(类型参数是协变的?逆变的?不变的?)
- 它不要求规范修改为新的文本,说明如何处理此类案件
- 它使代码能够自我记录——您不必学习所说的文本;目前关于显式接口实现的规则已经足够了
- 它不会给C#编译器团队带来开发成本(文档、功能实现、测试等)
反对:
- 你需要多打字
考虑到上述情况,再加上这不是一个日常场景的事实,我想得出的结论是明确的:这可能很好,但它肯定不能保证你会不遗余力地实现它。你可以通过下面的代码得到你想要的。通过在接口防御中包含类型参数,可以使其协变,这似乎满足编译器的要求。Base
类保持不变,您可以隐藏Base
实现,并使用单个方法实现接口
class Program
{
static void Main()
{
var something = new Something<Foo>();
var baseClass = (BaseClass)something;
var isomething = (ISomething<Foo>)something;
var baseResult = baseClass.DoSomething<Bar>();
var interfaceResult = isomething.DoSomething<Bar>();
var result = something.DoSomething<Bar>();
}
}
class Foo
{
}
class Bar : Foo
{
}
class BaseClass
{
public T DoSomething<T>()
{
return default(T);
}
}
interface ISomething<out T> where T : Foo
{
T DoSomething<T>();
}
class Something<T> : BaseClass, ISomething<T> where T : Foo
{
public new T DoSomething<T>()
{
return default(T);
}
}
具有相同签名的方法是否与实现接口相同?这里提出了相同的问题(尽管我不打算投票关闭此方法,因为在原始版本中似乎没有达成共识),就我个人而言,我倾向于汉斯·帕桑(Hans Passant)的回答,但实际上,如果你想要一个明确的答案,你可能需要像利珀特(Lippert)这样的人参与进来,为什么签名必须匹配?因为将something
引用为BaseClass
的某个实例可以尝试使用不继承Foo
@Ginosaji:的类型参数调用DoSomething
,这会有什么问题?这样做会违反Foo
对ISomething
的约束。很难用你的例子来演示,因为这只是一个动作。但是想象一下DoSomething
返回了T
的一个实例。您如何保证从Foo
继承的DoSomething
的返回值?这不是一个通用的解决方案,它有很多问题(例如,不是基类
,需要保持重复状态,无法访问受保护的
成员等)。@Jon了解这些限制。但是如果OP想要松耦合,那么它不需要是基类,事实上它不应该是。为了访问受保护的成员,他可以扩展它并扩大对成员的访问,然后包装该类的一个实例(我意识到是crufty)。但是,不知道您提到的复制状态在哪里。假设BaseSomething
具有状态,在这种情况下,您很容易被强制复制它。不管怎样,我的观点是关于一般情况的(而且只是因为你提供了一个具体的解决方案——这里有很多未知因素)。我同意构图对解决这个问题很有用,但它对我来说并不是一个好的解决方案。接口中定义的方法中只有少数与基类中定义的方法相冲突,因此组合它意味着复制每个方法。正如Jon所说,我将失去访问受保护成员的能力,这是当前实现所必需的。例如
public class Something : ISomething
{
private readonly BaseClass inner = ...;
void DoSomething<T>() where T : Foo
{
inner.DoSomething<T>();
}
}
class Program
{
static void Main()
{
var something = new Something<Foo>();
var baseClass = (BaseClass)something;
var isomething = (ISomething<Foo>)something;
var baseResult = baseClass.DoSomething<Bar>();
var interfaceResult = isomething.DoSomething<Bar>();
var result = something.DoSomething<Bar>();
}
}
class Foo
{
}
class Bar : Foo
{
}
class BaseClass
{
public T DoSomething<T>()
{
return default(T);
}
}
interface ISomething<out T> where T : Foo
{
T DoSomething<T>();
}
class Something<T> : BaseClass, ISomething<T> where T : Foo
{
public new T DoSomething<T>()
{
return default(T);
}
}
class Program
{
static void Main()
{
var something = new Something();
var baseClass = (BaseClass)something;
var isomething = (ISomething)something;
var baseResult = baseClass.DoSomething<Bar>();
var interfaceResult = isomething.DoSomething<Bar>();
var result = something.DoSomething<Bar>();
}
}
class Foo
{
}
class Bar : Foo
{
}
class BaseClass
{
public T DoSomething<T>()
{
return default(T);
}
}
interface ISomething
{
T DoSomething<T>;
}
interface ISomething<S> : ISomething where S : Foo
{
new R DoSomething<R>() where R : Foo;
}
class Something : BaseClass, ISomething
{
public new T DoSomething<T>()
{
return default(T);
}
}