代码之家  ›  专栏  ›  技术社区  ›  Andrey

接口是否从System.Object派生?规范说是,埃里克说不是,现实说不是

  •  32
  • Andrey  · 技术社区  · 14 年前

    C#4.0规范规定:(§4.2.2)

    所有其他类型的基类。每 直接或间接输入C

    Eric Lippert says

    接口类型,不是类, 不是从对象派生的。

    Type t = typeof(ICloneable).BaseType;
    Console.WriteLine(t == null);
    

    是的

    3 回复  |  直到 5 年前
        1
  •  36
  •   Jon Skeet    12 年前

    这并不像你想的那么简单:)

    接口没有 得到 object 对象 ToString() IDisposable ,例如。

    不幸的是,我认为规范的第4.2.2节过于简化了。希望Mads和Eric能在将来的版本中修复它-我会给他们发邮件,确保他们看到这个问题。

    我也在努力寻找规范中的任何东西来支持这个答案的其余部分。C#4规范的第3.4.5节非常接近我所能找到的:

    接口的成员是在接口和接口的所有基本接口中声明的成员。班上的成员 对象 可通过任何接口类型(7.4)中的成员查找获得。

    从接口类型到 包含在第6.1.6节:

    隐式引用转换为:

    • 从任何 引用类型 对象 dynamic
        2
  •  35
  •   Eric Lippert    4 年前

    乔恩(像往常一样)很在行。这并不像你想的那么容易!

    这个规范模糊不清,有点矛盾。在这种特殊情况下,最好眯着眼睛看一看,了解一下规范的要点 传达的方式 而不是狭义地解析它以获得精确的定义。

    接口实现 . 继承是*类(和委托)和结构(和枚举)类型的代码共享技术“;其机理是 . 这与 哪个是 . 这两件事在我看来在概念上很不一样;一个是关于 另一个是关于

    但是,规范没有这样做;它把两者结合在继承的范畴下。鉴于这两个有些不同的东西在规范中具有相同的名称,很难对它们之间的差异进行清楚而准确的推理。

    我个人更倾向于认为这个物体 任何接口的“基类型”,以及对象的成员 由接口继承。您可以在接口的实例上调用它们更像是编译器对您的一种礼貌,这样您就不必在其中插入cast to对象。


    更新:我注意到对于新读者来说,这个答案是在默认接口实现被添加到C#之前十年编写的。这个新特性与这里提出的问题没有直接关系。然而,它确实使水更加浑浊!

    在实现类可能“继承”而不仅仅是 要求 提供了一个接口成员的实现,同时也是一个 实施 那个成员的名字。这感觉更像是我们传统上认为的“继承”。

    即使是在这种新的、稍显混乱的情况下,我建议我们继续谨慎地使用行话:

    • 继承权仍然是 一种类型的成员也是另一种类型的成员 .
    • 任何接口类型的表达式仍然是 敞篷车 引用 object ,可能是 null
    • 编译器仍然允许您调用 对象 当接收器是任何接口类型的表达式时。
        3
  •  2
  •   svick Raja Nadar    12 年前

    接口类型不从继承 Object ,但接口类型的存储位置保存对类类型对象的引用,这些类类型对象(如果非null)保证从 System.Object .

    我认为,如果一个人从检查值类型和类类型之间的差异开始,那么理解正在发生的事情将是最容易的。假设我有一个结构:

    public struct SimplePoint {public int x,y;}
    

    我有两种方法

    public doSomethingWithPoint(SimplePoint pt) ...
    public doSomethingWithObject(Object it) ...
    

    以及每种方法:

    SimplePoint myPoint = ...;
    doSomethingWithPoint(myPoint);
    dosomethingWithObject(myPoint);
    

    第一个调用不会传递从 . 而是传递所有 SimplePoint 的公共和私有字段。第二次呼叫需要一个从 ,因此它将生成一个类型为的新堆对象实例 简单点 ,并用中的相应值加载所有这些字段 myPoint

    请注意,类型 实际上描述了两种不同的类型:字段集合(即值类型)和堆对象类型。哪种含义适用取决于使用类型的上下文。

    接口类型也有类似的缺点:当用作存储位置类型时,它们指定存储位置将包含对象引用。当用作泛型约束时,它们没有说明如何存储类型。因此,接口类型的存储位置将保存对堆对象的引用,该堆对象确实继承自 ,但约束到接口的类型的变量可能包含一个引用或一组字段。

        4
  •  0
  •   JacquesB    4 年前

    • 接口是a吗 子类型 属于 System.Object

    • Does接口 继承 系统对象 ? 不。

    subtype

    根据这个定义,C#中的所有接口都是子类型 系统对象 .

    例如,您可以执行以下操作:

     IComparable x = new String();
     Console.Write(x is Object); // writes "true"
    

    还有这个:

    IComparable x = new String();
    object y = x; // implicit cast from interface to System.Object
    

    以及:

    IComparable x = new String();
    var y = x.ToString(); // method inherited from System.Object
    

    但埃里克·利珀特说。。。

    在引号中,Lippert使用“派生”来表示继承。所有类类型都继承(直接或间接)来自 -但接口却不是。接口仅从其他接口继承。所以根据这个定义,接口显然 .

    但请注意,这是

    这两个定义在C#中通常是等价的,但你的问题是一个边缘情况,它会产生不同。

    反射是一种独立于语言的.net API,它使用CLI术语。这不能直接传输到C#,因为C#在形式上并不依赖于CLI(例如,在CLR中,值类型被视为与装箱值类型不同的类型,并且只有装箱值类型被视为对象。这不是C的区别。)

    BaseType 属性的指定方式如下:

    当前类型表示对象类或接口。

    可以 是否有基类型,只是不管它是否返回null。所以它并没有真正回答这个问题。

    底线

    最后,重要的是语言的可观察行为。语言设计者的意图可以从一个更大的引语中看出:

    C类型系统是统一的,因此任何类型的值都可以 被当作物体对待。C#中的每种类型都直接或间接地派生 object 以类型查看值 对象 通过执行装箱和拆箱操作作为对象(§9.3.12).

    重要的一点是,接口始终可以被视为对象。它们是否“真的”来自于客体,这纯粹是哲学上的讨论。