接口中的类型约束适用于基类
本文关键字:适用于 基类 约束 类型 接口 | 更新日期: 2023-09-27 17:56:45
我有一个基类,它定义了一个泛型方法,如下所示:
public class BaseClass
{
public T DoSomething<T> ()
{ ... }
}
由于此类由第三方提供并且不附带接口,因此我正在定义一个接口,该接口定义了该类中实际需要的方法。这样我就可以松散耦合,实际上可以将该第三方类与其他类交换。对于此示例,请考虑以下接口:
public interface ISomething
{
T DoSomething<T> ()
where T : Foo;
}
如您所见,它定义了相同的方法,但也对类型参数应用了类型约束,该约束来自与此无关的其他一些要求。
接下来,我定义一个 BaseClass
的子类型,它也实现了 ISomething
。此类将用作接口后面的常规实现,而接口将是应用程序的其余部分将访问的内容。
public class Something : BaseClass, ISomething
{
// ...
}
由于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>();
}
两者都有效,尽管我可能更喜欢第二种解决方案来保持该方法可从类本身访问。但是,它仍然留下以下问题:
当基类型已经使用不太严格(读取:无)的类型约束实现该方法时,为什么我必须重新实现该方法?为什么该方法需要完全按原样实施?
编辑:为了使该方法更有意义,我将返回类型从 void
更改为 T
。在我的实际应用程序中,我既有泛型参数又有返回值。
尝试使用组合而不是继承来实现Something
:
public class Something : ISomething
{
private readonly BaseClass inner = ...;
void DoSomething<T>() where T : Foo
{
inner.DoSomething<T>();
}
}
当然,给定的代码可以安全地编译和运行:
当一个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);
}
}
或者,如果您真的不想在实例化中指定Foo
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);
}
}