关于.NET中接口继承的问题

3

让我们以接口IQueryable<T>为例:

public interface IQueryable<T> : IQueryable, IEnumerable;

由于IQueryable本身实现了IEnumerable:

public interface IQueryable : IEnumerable;

为什么在第一个声明中,IQueryable必须显式实现IEnumerable呢?这与类继承不同,后者不需要显式地继承“祖父类”?

"IQueryable必须显式实现IEnumerable" -- 来源在哪里?“显式实现”是什么意思? - strager
在 MSDN 文档中,如果您查看 IQueryable<T> 的声明,您会看到类似于我帖子中第一个声明的内容,其中 IQueryable<T> 明确实现了 IQueryable 和 IEnumerable 两个接口,我觉得这是多余的。 - Aperture
这是因为像C#这样的语言支持接口继承,但IL不支持...所以编译器在编译代码时会直接为您明确实现所有接口。 - Pauli Østerø
请查看Pauli的解释和我的示例。您将看到文档和元数据是如何生成的。 - anon
4个回答

3
提及祖辈(以及更高的祖先)在声明中不是必须的,但由于父级是一个接口,实际上并没有实现祖辈,最好在孙子的声明中提及祖辈以改善文档说明,以备最终的实现者使用。
这就是为什么在祖先的具体实现方面,你会在MSDN中看到以下内容:
public class Button : ButtonBase,IButtonControl

and not the following ...

public class Button : ButtonBase,Control,Component,MarshalByRefObject,Object

2

这不是真的。我怀疑你在Visual Studio中右键点击这些类/接口,然后点击“转到定义”,这就是元数据告诉你的。如果你想真正理解发生了什么,这里有一个例子:

    public interface IAddTwo
    {
        int AddTwo(int x);
    }

    public interface IAddTwoOrThree : IAddTwo
    {
        int AddThree(int y);
    }

    public class AddTwoOrThree : IAddTwoOrThree
    {
        public int AddTwo(int q)
        {
            return q + 2;
        }

        public int AddThree(int z)
        {
            return z + 3;
        }
    }

请注意,最后一个类只需要实现一个接口(但如果您省略任一方法,则会收到编译器错误)。
将上述内容创建为一个类库,通过浏览所得的DLL添加引用(而不是通过向项目添加引用),并查看AddTwoOrThree的元数据信息。 您将看到:
AddTwoOrThree : IAddTwoOrThree, IAddTwo

哇!这就是为什么你在MSDN上看到的东西是这样的。他们展示的是元数据,而不是他们的程序员编写的代码。


嗨,如果您在MSDN页面http://msdn.microsoft.com/en-us/library/bb351562.aspx的语法部分下查看,可以发现IQueryable<T>的声明类似于我帖子中的第一个声明。因此我有这个问题。 - Aperture
1
不需要。他们的帮助页面只是显示每个类/接口实现的元数据,但您不必在代码中显式实现接口。如果您想了解发生了什么,请创建一个具有两个项目的Visual Studio解决方案。使用我的上述代码创建一个类库。然后创建一个新项目,并通过使用BROWSE(而不是添加项目引用)向另一个DLL添加引用。现在实例化AddTwoOrThree。当您右键单击并选择“转到定义”时,您可能会对所看到的内容感到惊讶。 - anon

1

这是因为接口继承是一种纯语言糖,当编译成IL时,该接口会显式实现链中的所有接口,因此查看IQueryable<T>的签名最终会变成:

.class public interface abstract auto ansi IQueryable<+ T>
    implements [mscorlib]System.Collections.Generic.IEnumerable`1<!T>, System.Linq.IQueryable, [mscorlib]System.Collections.IEnumerable
{
}

由于大多数文档都是从元数据自动生成的,而不是从原始源代码生成的,因此最终会显示签名作为直接实现所有接口,而不是通过继承。这基本上就是它的工作方式。您之所以能够利用继承的唯一原因是编译器最终会为您创建正确的签名,并明确连接所有接口。

0

正确的定义是:

public interface IQueryable<out T> : IEnumerable<T>, IQueryable { }

以下内容摘自Microsoft的Source RTMRel\ndp\fx\src\Core\System\Linq\IQueryable.cs\1305376\IQueryable.cs。
    public interface IQueryable : IEnumerable {
        Expression Expression { get; } 
        Type ElementType { get; } 

        // the provider that created this query 
        IQueryProvider Provider { get; }
    }

#if SILVERLIGHT 
    public interface IQueryable<T> : IEnumerable<T>, IQueryable {
#else 
    public interface IQueryable<out T> : IEnumerable<T>, IQueryable { 
#endif
    } 

    public interface IQueryProvider{
        IQueryable CreateQuery(Expression expression);
        IQueryable<TElement> CreateQuery<TElement>(Expression expression); 

        object Execute(Expression expression); 

        TResult Execute<TResult>(Expression expression);
    } 

    public interface IOrderedQueryable : IQueryable {
    }

#if SILVERLIGHT
    public interface IOrderedQueryable<T> : IQueryable<T>, IOrderedQueryable { 
#else 
    public interface IOrderedQueryable<out T> : IQueryable<T>, IOrderedQueryable {
#endif 
    }

嗨,如果您在MSDN页面http://msdn.microsoft.com/en-us/library/bb351562.aspx的语法部分下查看,可以发现IQueryable<T>的声明与我帖子中的第一个声明类似。因此我有这个问题。 - Aperture
不要在公共论坛(如SO)上发布.Net的源代码。这会使那些已经阅读过这样的源代码的人无法为Mono做出贡献。http://mono-project.com/Contributing 如果您查看了Microsoft专有的.NET实现或其共享源代码(也是专有的),则将无法为Mono做出贡献。如果您想在这个领域做出贡献,请注意不要在Visual Studio调试体验中查看它们。 - Pauli Østerø
@Pauli Østerø:我没有发布源代码。我引用了源代码,所以任何人都可以查看它。我发布了接口定义,可以通过反射或对象浏览器获取。 - AMissico
根据我的经验和理解,他们的意思是...在贡献代码时不要访问专有代码。你可以整天阅读代码。你甚至可以利用从阅读中获得的知识,但你绝不能使用实际的源代码作为参考或任何其他工具来确定实现细节。 - AMissico

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