Fabulous Adventures In Coding
Eric Lippert is a principal developer on the C# compiler team. Learn more about Eric.
I hear a lot of myths about C#. Usually the myths have some germ of truth to them, like "value types are always allocated on the stack". If you replace "always" with "sometimes", then the incorrect mythical statement becomes correct.
One I hear quite frequently is "in C# every type derives from object". Not true!
First off, no pointer types derive from object, nor are any of them convertible to object. Unsafe pointer types are explicitly outside of the normal type rules for the language. (If you want to treat a pointer as a value type, convert it to System.IntPtr, and then you can use it as a value type.) We'll leave pointer types aside for the rest of this discussion.
A great many types do derive from object. All value types, including enums and nullable types, derive from object. All class, array and delegate types derive from object.
These are all the "concrete" types; every object instance that you actually encounter in the wild will be either null, or a class, delegate, array, struct, enum or nullable value type. So it would be correct, though tautological, to say that all object instances derive from object. But that wasn't the myth; the myth stated was that all types are derived from object.
Interface types, not being classes, are not derived from object. They are all convertible to object, to be sure, because we know that at runtime the instance will be a concrete type. But interfaces only derive from other interface types, and object is not an interface type.
"Open" type parameter types are also not derived from object. They are, to be sure, types. You can make a field whose type is a type parameter type. And since generic types and methods ultimately are only ever used at runtime when fully "constructed" with "concrete" type arguments, type parameter types are always convertible to object. (This is why you cannot make an IEnumerable<void*> -- we require that the type argument be convertible to object.) Type parameter types are not derived from anything; they have an "effective base class", so that type arguments are constrained to be derived from the effective base class, but they themselves are not "derived" from anything.
The way to correct this myth is to simply replace "derives from" with "is convertible to", and to ignore pointer types: every non-pointer type in C# is convertible to object.
"Interface types, not being classes, are not derived from object. They are all convertible to object,.."
I have a problem with this.
Interfaces in C# roughly model contracts. That is, I can explicitly state what my class will support via an interface.
Also consider the following code:
IContract contract = new A(); //A implements IContract and derives from object
Object o = contract;// “upcasting”
And therefore since the conversion in line 2 is legal in C# I am basically stuck with IContract being “forced” to expose the methods from Object like ToString(), GetHashCode() etc. This seems a little paradoxical to me since on the one hand an Interface should be a contract defined by me, but on the other hand the language is forcing me to include methods from System.Object in this contract. What if I do not want ToString() to be called via this interface to prevent serializing my object to a corresponding string? Should there not be a way to explicitly “shut off” these inherited methods specifically for interfaces?
My deskmate here in the office says that he had an COM object instance (implemented in delphi) which was not convertible to Object in C# (which makes sense, given that it does not expose those methods). So it seems that Interface types are not guaranteed to be convertible to object.
What about "types" like IDictionary<,>? Granted, that isn't a type in the language, and there is no value anywhere with that type, but there are instances/is an instance of System.Type that represents it. Another thing to watch out for in some reflection code paths, someone could pass you a "type" like that.
@cpun: The interface isn't inheriting or exposing members of Object. Object mandates that anything converted to it inherits these members.
@Markus: Implementation details are necessary to validate this claim. I'll bet there was an implementation error during the conversion process.
@Douglas: There is no type Dictionary<,>; you cannot declare an instance of or otherwise make any programmatic use of Dictionary<,>. There is a type Dictionary<TKey, TValue>, wherein the type parameters are suffused with existing types. The type parameters themselves act as placeholders for types to be defined by the programmer.
I've known for some time that interfaces were the exception to the "everything is an Object" rule, but I never considered these other cases. Enlightening.
@tom That still doesnt address my conceptual problem with why a contract would be forced to have those methods. Whether it be due to conversion or inheritance is an implementation detail.
@cpun: The *contract* isn't forced to have anything. An interface (contract) forces a class which implements it to have appropriate methods. When you implement a class, it derives from object, which already has some methods on it. You are certainly not obliged to override them if you don't want to.
It sounds like your argument is either that (a) it's not desirable to force all classes to derive from object, or (b) object should not have any methods. Both are valid questions to consider, but neither is directly related to interfaces.
While it might seem more "clean" to not have a common base class (object), or else to have a common base class but make it empty, as a practical matter there's very little downside to these rules and considerable practical benefit.
...not to forget that Object exposes Methods that would not have a home otherwise and that are core to the language(s), like Equals(object, object) and ReferenceEquals(object, object).
nice, good information, as always
I don't disagree with you that there is no such type. However, there is such a "type," which was the caution I was trying to point out. You'll find that typeof(IDictionary<,>) is an instance of System.Type with some unusual properties, that's all I'm trying to say.
That type does indeed exist. Incomplete generic types are accessible via reflection, and can be very useful in some cases. For example, you can use this to do instantiate generic types whose type parameters are only known at runtime.
I thought my point was clear and yes it is pertinent to mostly interfaces which are meant to describe a class saying "i will provide this service to the world". Because of the convertibility to object the problem now is that they have these methods that are infact forced onto the interface. Reminds me of COM interfaces and the AddRef, Release etc methods which were ugly.
Not convinced? How is this:
ICustomer customer = createCustomer();
customer.GetHashCode();//makes as much sense as having horns on a fish..
Most people don't use words careful enough, that's why they say "all types derive from object". What they meant to say is: "all classes inherit from object" -- and this is always true, unless you start shifting definitions of "class", "inherits" and "object". But then again, I can come up with a new more precise statement that will still be valid. This does not mean my idea behind the statement was invalid, it just means I need a more precise wording.
>"Open" type parameter types are also not derived from object. They are, to be sure, types. You can make a field whose type is a type parameter type.
Reflection does not agree with you there - Type.BaseType where T is an type parameter without any constraints, returns System.Object.
@zvolkov you're absolutely correct, but the issue arises because there are the people who understand and use the terms interchangeably, relying on other readers to be like themselves and understand in context of the communication.
However it's the people who don't understand the differences that the clarification is needed, and they run into trouble, or start making 1/2 correct inferences and mis-communicating to others.
It pays to be precise, and when in the habit of using the correct terminology, comprehension becomes easier for all parties.
I'm surprised you didn't mention delegate types in your discussion.