繁体   English   中英

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

[英]Type constraints in interface apply to base class

我有一个基类定义了这样的泛型方法:

public class BaseClass
{
    public T DoSomething<T> ()
    { ... }
}

由于这个类是由第三方提供的,并没有附带接口,因此我定义了一个接口,用于定义该类中实际需要的方法。 这样我就可以得到松散的耦合,并且实际上可以用其他东西来交换第三方类。 对于此示例,请考虑以下接口:

public interface ISomething
{
    T DoSomething<T> ()
        where T : Foo;
}

如您所见,它定义了相同的方法,但也对类型参数应用了类型约束,该类型参数来自与此无关的其他一些要求。

接下来,我定义了BaseClass的子类型,它也实现了ISomething 该类将用作接口背后的通常实现 - 而接口将是应用程序的其余部分将访问的内容。

public class Something : BaseClass, ISomething
{
    // ...
}

由于BaseClassDoSomething已经支持任何类型参数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>();
}

两者都有效,虽然我可能更喜欢第二种解决方案,以保持方法可以从类本身访问。 但是它仍然留下以下问题:

当基类型使用less-strict(read:none)类型约束实现它时,为什么必须重新实现该方法? 为什么该方法需要完全按原样实现?

编辑:为了给方法更多的意义,我将返回类型从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#编译器团队施加开发成本(文档,功能实现,测试等)

反对:

  • 你需要输入更多

考虑到上述情况以及此事实并非日常情况,恕我直言会得出的结论很明确:这可能会很好,但它肯定不会让您不顾一切地实施它。

您可以使用下面的代码获得所需内容。 通过在接口defenition中包含type参数,您可以使其协变,这似乎满足编译器。 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);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM