简体   繁体   English

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

[英]Type constraints in interface apply to base class

I have a base class that defines a generic method like this: 我有一个基类定义了这样的泛型方法:

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

As this class is by a third-party and does not come with an interface, I am defining an interface that defines the actually needed methods from that class. 由于这个类是由第三方提供的,并没有附带接口,因此我定义了一个接口,用于定义该类中实际需要的方法。 That way I get loose coupling and can actually exchange that third-party class with something else. 这样我就可以得到松散的耦合,并且实际上可以用其他东西来交换第三方类。 For this example, consider the following interface: 对于此示例,请考虑以下接口:

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

As you can see, it defines the same method but also applies a type constraint on the type parameter, which comes from some other requirements that are not relevant to this. 如您所见,它定义了相同的方法,但也对类型参数应用了类型约束,该类型参数来自与此无关的其他一些要求。

Next, I define a subtype of BaseClass which also implements ISomething . 接下来,我定义了BaseClass的子类型,它也实现了ISomething This class will be used as the usual implementation behind the interface–while the interface will be what the rest of the application will be accessing. 该类将用作接口背后的通常实现 - 而接口将是应用程序的其余部分将访问的内容。

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

As the DoSomething in BaseClass already supports any type parameter T , it should especially support a type parameter which is a subtype of Foo . 由于BaseClassDoSomething已经支持任何类型参数T ,它应该特别支持一个类型参数,它是Foo的子类型。 So one would expect that a subtype of BaseClass already implements the interface. 因此可以预期BaseClass的子类型已经实现了接口。 However I get the following error: 但是我收到以下错误:

The constraints for type parameter 'T' of method 'BaseClass.DoSomething()' must match the constraints for type parameter 'T' of interface method 'ISomething.DoSomething()'. 方法'BaseClass.DoSomething()'的类型参数'T'的约束必须匹配接口方法'ISomething.DoSomething()'的类型参数'T'的约束。 Consider using an explicit interface implementation instead. 请考虑使用显式接口实现。

Now, I have two possibilities; 现在,我有两种可能性; the first one is to do what the error suggests and implement the interface explicitely. 第一个是做错误建议并明确地实现接口。 The second is to hide the base implementation using new : 第二种是使用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>();
}

Both work, although I'd probably prefer the second solution to keep the method accessible from the class itself. 两者都有效,虽然我可能更喜欢第二种解决方案,以保持方法可以从类本身访问。 However it still leaves the following question: 但是它仍然留下以下问题:

Why do I have to re-implement the method when the base type already implements it with a less-strict (read: none) type constraint? 当基类型使用less-strict(read:none)类型约束实现它时,为什么必须重新实现该方法? Why does the method need to be implemented exactly as it is? 为什么该方法需要完全按原样实现?

edit: To give the method a bit more meaning, I changed the return type from void to T . 编辑:为了给方法更多的意义,我将返回类型从void更改为T In my actual application, I have both generic arguments and return values. 在我的实际应用程序中,我有泛型参数和返回值。

Try using composition instead of inheritance to implement Something : 尝试使用组合而不是继承来实现Something

public class Something : ISomething
{
    private readonly BaseClass inner = ...;

    void DoSomething<T>() where T : Foo
    {
        inner.DoSomething<T>();
    }
}

Certainly the given code could be compiled and run safely: 当然,给定的代码可以编译并安全运行:

When a Something instance is typed as Something or as BaseClass the compiler would allow any type for T , while when the same instance is typed as ISomething it would allow just types inheriting Foo . Something实例被输入SomethingBaseClass ,编译器将允许T任何类型,而当同一实例被输入为ISomething ,它将只允许继承Foo类型。 In both cases you get static checking and runtime safety as usual. 在这两种情况下,您都可以照常进行静态检查和运行时安

In fact, the above scenario is exactly what happens when you implement ISomething explicitly. 实际上,上面的场景正是您明确实现ISomething时会发生的情况。 So let's see what arguments we can make for and against the current state of affairs. 那么让我们看看我们可以为当前的事态做出什么样的争论。

For: 对于:

  • the proposed solution would not be applicable to all cases; 拟议的解决方案不适用于所有情况; it depends on the exact method signatures (is the type argument covariant? contravariant? invariant?) 它取决于确切的方法签名(类型参数协变?逆变?不变?)
  • it doesn't require the specification to be amended with new text stating how such cases are handled 它不要求用新文本修改规范,说明如何处理这些案件
  • it makes the code self-documenting -- you don't have to learn said text; 它使代码自我记录 - 你不必学习所说的文本; the current rules regarding explicit interface implementation are enough 关于显式接口实现的当前规则就足够了
  • it does not impose development costs on the C# compiler team (documentation, feature implementation, testing, etc) 它不会对C#编译器团队施加开发成本(文档,功能实现,测试等)

Against: 反对:

  • you need to type more 你需要输入更多

Considering the above and additionally the fact that this is not an everyday scenario, IMHO the conclusion to be reached is clear: this might be nice to have, but it certainly doesn't warrant going out of your way to implement it. 考虑到上述情况以及此事实并非日常情况,恕我直言会得出的结论很明确:这可能会很好,但它肯定不会让您不顾一切地实施它。

You can get what you want with the code below. 您可以使用下面的代码获得所需内容。 By including the type parameter in the interface defenition you can make it covariant which seems to satisfy the compiler. 通过在接口defenition中包含type参数,您可以使其协变,这似乎满足编译器。 The Base class remains untouched and you are able to shadow the Base implementation and implement the interface with a single method. 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);
    }
}

Or if you really don't want to specify Foo in the instantiation 或者如果你真的不想在实例化中指定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