接口中的类型约束适用于基类

本文关键字:适用于 基类 约束 类型 接口 | 更新日期: 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实例被类型化为SomethingBaseClass编译器将允许任何类型用于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);
    }
}