开发者

Do interfaces derive from System.Object? C# spec says yes, Eric says no, reality says no

开发者 https://www.devze.com 2023-01-07 15:02 出处:网络
Question is simple and asked in the title. C# 4.0 Specification says: (§4.2.2) The object class type is the ultimate

Question is simple and asked in the title.

C# 4.0 Specification says: (§4.2.2)

The object class type is the ultimate base class of all other types. Every type in C# directly or indirectly derives from the object class type.

Eric Lippert says:

Interface types, not being classes, 开发者_开发百科 are not derived from object.

Reality says:

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

True

So is spec wrong or what? Whom to believe?


It's not quite as simple a question as you might think :)

Interfaces don't derive from object but you can call the members of object on them. So you can call ToString() on an expression which has a compile-time type of IDisposable, for example.

Coincidentally, I overhead a conversation between Neal Gafter and Eric at NDC discussing exactly this point...

I believe section 4.2.2 of the spec is over simplified, unfortunately. Hopefully Mads and Eric will fix it up for a future release - I'll mail them to make sure they see this question.

I'm also struggling to find anything in the spec to back up the rest of this answer. Section 3.4.5 of the C# 4 spec comes as close as I can find:

The members of an interface are the members declared in the interface and in all base interfaces of the interface. The members in class object are not, strictly speaking, members of any interface (13.2). However, the members in class object are available via member lookup in any interface type (7.4).

The conversion from an interface type to object is covered by section 6.1.6:

The implicit reference conversions are:

  • From any reference-type to object and dynamic.


Jon is (as usual) spot on. It is not as easy as you think!

The spec is vague and slightly contradictory. In this particular case it is probably best to squint a little bit and get the gist of what the spec means to convey rather than narrowly parsing it for precise definitions.

The simple fact of the matter is that "inheritance" is a very overused term in object-oriented programming. (I seem to recall that C++ has six different kinds of inheritance, though I'd be hard pressed to name them all on short notice.)

If I had my druthers then the C# specification would clearly call out a difference between inheritance and interface implementation. Inheritance is *a code-sharing technique for class (and delegate) and struct (and enum) types"; its mechanism is that all heritable members of a base type become members of a derived type. That is in contrast with interface implementation which is a requirement that an implementing type have a certain set of public members. Those two things seem conceptually very different to me; one is about sharing existing members and the other is about requiring certain members.

However, the spec does not do so; it conflates the two under the rubric of inheritance. Given that these two somewhat different things have the same name in the spec, it is hard to reason clearly and precisely about the differences between them.

I personally prefer to think that object is not the "base type" of any interface, and that the members of object are not inherited by the interface. That you can call them on an instance of an interface is more like a courtesy extended to you by the compiler so that you do not have to insert a cast to object in there.


UPDATE: I note for new readers that this answer was written ten years before default interface implementations were added to C#. This new feature does not bear directly upon the question that was asked here. However, it does muddy the waters even more!

Now we are in a situation where implementing classes may "inherit" not just the requirement to provide an implementation of an interface member, but also an implementation of that member. This feels much more like what we would traditionally think of as "inheritance".

Even in this new and slightly more confusing situation, I recommend that we continue to use jargon carefully:

  • Inheritance continues to be the property that members of one type are also members of another type.
  • It is still the case that any expression of any interface type is convertible to a reference to object, which may be null.
  • It is still the case that the compiler will allow you to call members of object when the receiver is an expression of any interface type.


Interface types do not inherit from Object, but storage locations of interface types hold references to class-type objects which (if non-null) are guaranteed to inherit from System.Object.

I think understanding what's going on will be easiest if one starts by examining the difference between value types and class types. Suppose I have a structure:

public struct SimplePoint {public int x,y;}

and I have two methods

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

and cal each method:

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

The first call does not pass a thing that derives from Object. It instead passes the contents of all of SimplePoint's public and private fields. The second call needs a thing which derives from Object, so it generates a new heap object instance of type SimplePoint which contains all the public and private fields of the value-type SimplePoint, and loads all those fields with the corresponding values from myPoint, and passes a reference to that object.

Note that the type SimplePoint actually describes two different kinds of things: a collection of fields (i.e. the value type) and a heap-object type. Which meaning is applicable depends upon the context where the type is used.

Interface types have a similar wrinkle: when used as storage-location types, they specify that the storage location will hold an object reference. When used as a generic constraint, they say nothing about how the type will be stored. Thus, a storage location of an interface type will hold a reference to a heap object that genuinely does inherit from System.Object, but a variable of a type constrained to an interface might hold either a reference or a bunch of fields.


It really depends on the definition of "derives". Surprisingly, the C# specification does not have a single canonical definition for this term. Fortunately the actual behavior of code is still specified unambiguously. But it does mean some questions are hard to answer without going into hair-splitting!

  • Are interfaces a subtype of System.Object? Yes.

  • Does interfaces inherit from System.Object? No.

The common definition of subtype is that if type A is a subtype of type B, then a value of type A can be used anywhere a value of type B is expected. A fullfills the contract of B.

By this definition, all interfaces in C# are subtypes System.Object since an interface instance is also an instance of System.Object.

For example you can do this:

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

and this:

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

and:

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

So it walks like a duck and quacks like a duck!

But Eric Lippert says...

In the quote Lippert is using "derived" to refer to inheritance. All class types inherit (directly or indirectly) members from System.Object - but interfaces does not. Interfaces only inherit from other interfaces. So by this definition, interfaces clearly does not derive from System.Object.

But note that this is not a disagreement about how the language actually works, it is purely a difference in the terminology used to describe it.

The two definitions are usually equivalent in C#, but your question is one of the edge cases where it makes a difference.

But reality says...

Reflection is a language-independent .net API which uses the CLI terminology. This cannot be transferred directly to C# since C# is not formally depending on the CLI. (For example in CLR value types are considered different types than boxed value types, and only boxed value types are considered objects. This is not a distinction C# makes.)

But anyway, the BaseType property is specified like this:

The Type from which the current Type directly inherits, or null if the current Type represents the Object class or an interface.

Note that this completely sidesteps the issue! It doesn't state whether interfaces can have a base type or not, just that it will always return null regardless. So it doesn't really answer the question.

Bottom line

In the end, what matters is the observable behavior of the language. The intention of the language designers is clear from a larger quote:

C#’s type system is unified such that a value of any type can be treated as an object. Every type in C# directly or indirectly derives from the object class type, and object is the ultimate base class of all types. Values of reference types are treated as objects simply by viewing the values as type object. Values of value types are treated as objects by performing boxing and unboxing operations (§9.3.12).

The important point is that interfaces can always be treated as objects. Whether they "actually" derive from object is purely a philosophical discussion then.

0

精彩评论

暂无评论...
验证码 换一张
取 消