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);
    }
}