带有泛型参数的接口 vs 带有泛型方法的接口

54

假设我有这样的接口和具体实现:

public interface IMyInterface<T>
{
    T My();
}

public class MyConcrete : IMyInterface<string>
{
    public string My()
    {
        return string.Empty;
    }
}

我创建了一个strings的实现,这样我可以再为int创建一个具体实现。这是可以的。但是假设我想用通用方法来做同样的事情,那么我就有:

public interface IMyInterface2
{
    T My<T>();
}

public class MyConcrete2 : IMyInterface2
{
    public string My<string>()
    {
        throw new NotImplementedException();
    }
}

我有一个类似的 IMyInterface2 接口,它通过 T My<T>() 定义了通用行为。在我的具体类中,我想要实现 My 行为,但使用具体的数据类型 - string。但是 C# 不允许我这样做。

我的问题是为什么我不能这样做? 换句话说,如果我可以将 MyInterface<T> 的具体实现创建为 MyClass : MyInterface<string> 并在此处停止泛型性,为什么我不能对泛型方法 T My<T>() 进行同样的操作?


2
在继承类型时,您不能“减少”能力,只能增加它们。 - Lasse V. Karlsen
4个回答

49

你的通用方法实现也必须是通用的,因此它必须是:

public class MyConcrete2 : IMyInterface2
{
    public T My<T>()
    {
        throw new NotImplementedException();
    }
}

为什么你不能在这里执行 My<string>()?因为接口契约需要一个方法,该方法可以使用任何类型参数 T 调用,并且您必须履行该契约。
为什么你不能在这个点停止泛型化?因为这会导致以下情况:
类声明:
public interface IMyInterface2
{
    T My<T>(T value);
}

public class MyClass21 : IMyInterface2
{
    public string My<string>(string value) { return value; }
}

public class MyClass22 : IMyInterface2
{
    public int My<int>(int value) { return value; }
}

用法:

var item1 = new MyClass21();
var item2 = new MyClass22();

// they both implement IMyInterface2, so we can put them into list
var list = new List<IMyInterface2>();
list.Add(item1);
list.Add(item2);

// iterate the list and call My method
foreach(IMyInterface2 item in list)
{
    // item is IMyInterface2, so we have My<T>() method. Choose T to be int and call with value 2:
    item.My<int>(2);

    // how would it work with item1, which has My<string> implemented?
}

1
它需要是公共的 T My<T>() - Moo-Juice
如果我可以创建MyInterface<T>的具体实现,例如MyClass:MyInterface<string>并在此点停止泛型,为什么我不能使用泛型方法 - T My<T>()来做到这一点呢? - Jevgenij Nekrasov
1
@JevgenijNekrasov,请查看我的更新。我展示了一个情况,当你的My<string>实现会失败。 - MarcinJuraszek
同意。但实际上,在这种情况下可以使用实际类型,并使用对象的多态行为调用具体实现。 - Jevgenij Nekrasov
你想如何实现这个? - MarcinJuraszek
C# 不支持泛型方法的显式特化。 - DavidY

5

因为你的接口声明了一个通用方法T My<T>(),但你的实现没有实现这个特定签名的函数。

如果要达到你想要的效果,需要在第一个示例中将T泛型参数提供给接口:

public interface IMyInterface2<T>
{
        T My();
}

public class MyConcrete2 : IMyInterface2<string>
{
    public string My()
    {
        throw new NotImplementedException();
    }
}

我知道如何实现我想要的,但我想知道为什么我不能像处理接口一样停止通用性。 - Jevgenij Nekrasov

3

当你编写泛型方法时,定义用于保留占位符。实际类型在调用方法时才会出现。因此,你应该写成:

public T My<T>()
{
    throw new NotImplementedException();
}

当你调用该方法时,你可以在那里使用字符串。


我想停止泛型,就像我可以使用泛型接口一样,例如:MyClass : MyInterface<string>。 - Jevgenij Nekrasov

1
你的解决方案存在两个问题。
首先,接口是一种契约。当你实现了 IMyInterface2 接口后,你保证会实现一个名为 My 的函数,该函数带有一个泛型类型参数并返回该类型。而 MyConcrete2 并没有做到这一点。
其次,C# 泛型不允许任何类型参数特化。(我希望 C# 支持此功能。)这在 C++ 模板中很常见,你的示例可以编译通过,但如果没有使用 string 调用 My,则所有 MyConcrete2 的用法都将无法编译。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接