Partager via


Not everything derives from object

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.

Comments

  • Anonymous
    August 06, 2009
    The comment has been removed

  • Anonymous
    August 06, 2009
    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.

  • Anonymous
    August 06, 2009
    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.

  • Anonymous
    August 06, 2009
    @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.

  • Anonymous
    August 06, 2009
    @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.

  • Anonymous
    August 06, 2009
    @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.

  • Anonymous
    August 06, 2009
    ...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).

  • Anonymous
    August 06, 2009
    nice, good information, as always

  • Anonymous
    August 06, 2009
    @Tom 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.

  • Anonymous
    August 06, 2009
    @Tom 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.

  • Anonymous
    August 06, 2009
    @Tony cox 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..

  • Anonymous
    August 06, 2009
    The comment has been removed

  • Anonymous
    August 06, 2009
    >"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.

  • Anonymous
    August 06, 2009
    @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.

  • Anonymous
    August 06, 2009
    I'm surprised you didn't mention delegate types in your discussion.

  • Anonymous
    August 06, 2009
    The comment has been removed

  • Anonymous
    August 06, 2009
    @cpun: It sounds like what you really want is for Object's methods to be "hidden," most likely at the IDE level.  I actually think it would be great to bump up intellisense's intelligence and allow you to sort out some of the more obnoxious inherited members. At runtime, you may think you don't want those methods, but as sukru pointed out, you probably do, you just don't want to have to think about it.

  • Anonymous
    August 06, 2009
    The comment has been removed

  • Anonymous
    August 06, 2009
    @Tim Tool-nuisance and cluttering is definitely one aspect of it. Another is that it just does not make any sense to me, especially for interfaces. My comments above make clear why.

  • Anonymous
    August 06, 2009
    I'm surprised you didn't mention delegate types in your discussion..

  • Anonymous
    August 07, 2009
    what about the "void" type? why is not Func<void> allowed?

  • Anonymous
    August 07, 2009
    @Chris:  Thanks for the info.  I wasn't aware we could specify type parameters at run-time.

  • Anonymous
    August 07, 2009
    @cpun:  If you have this much enmity toward a harmless and potentially very useful feature, you're using the wrong language.  .NET is designed to simplify things.  This is accomplished in part by providing a variety of "default" methods so the user doesn't have to recreate them:  Equals(), GetHashCode(), MemberwiseClone().  If you have a problem with this, C++ and STL (or some other combination) is your answer.

  • Anonymous
    August 07, 2009
    If interface is not an Object why does this code print it is an object? interface ITest { void Run(); } class Program { static void Main(string[] args) { if(typeof(ITest) is Object) { Console.WriteLine("ITest is an Object."); } else { Console.WriteLine("ITest is not an Object."); } } }

  • Anonymous
    August 07, 2009
    @ James because typeof(ITest) returns an instance of System.Type representing the interface ITest, not the  ITest interface itself

  • Anonymous
    August 07, 2009
    @James: "typeof(ITest) is Object" is true because typeof(ITest) returns the instance of System.Type describing ITest.  And System.Type derives from Object.

  • Anonymous
    August 07, 2009
    The comment has been removed

  • Anonymous
    August 07, 2009
    The comment has been removed

  • Anonymous
    August 07, 2009
    @tony cox thanks for taking my comments in the spirit they were intended. I have been writing a library based data access DSL @work and since I use interfaces to model the BNF productions the fact that i get these methods ruining my otherwise nice DSL is ticking me off :). But as you said, everything is a trade-off.

  • Anonymous
    August 07, 2009
    @Tony Cox, What you propose was not the only alternative design. A better approach would have been to make GetHashCode and ToString static members of Object. The implementation would first check for the IHashCodeProvider or IStringProvider interfaces; if they are implemented on the type, use them, otherwise use the built-in implementation. Not only would this have been a more "pure" design, but it would allow type consumers to determine whether or not a class provides its own implementation of these methods. This would be consistent with casting to any other interface to determine if a type has particular capabilites, like IComparable or INotifyPropertyChanged. This could be especially significant for ToString; there have been several times when I have wanted to display a string which represents an object, but if the object does not provide its own string implementation, I do NOT want to display the type name, which is the default implementation of ToString. If there were an IStringProvider interface, I could check for the existence of the interface and if its not there, provide my own implementation (such as a blank string).

  • Anonymous
    August 07, 2009
    @cpun Kzu have a blog post about how you can hide object memebers in interface based dsl:s here: http://www.clariusconsulting.net/blogs/kzu/archive/2008/03/10/58301.aspx As a side note IntelliSense in VB.Net these methods are infact NOT callable on an object of an interface type, you have to upcast to Object to use them.

  • Anonymous
    August 07, 2009
    I think the practical reality of an empty or nearly-empty "object" implementation would have been a wildly fragmented spinoff of innumerable very similar "rollup" classes that tried to ramp up a minimalist set of basic functionality, but all subtely mutually incompatible, or at worst 90% reinventions of very similar wheels. I think the design choice was the practical one, if not the philosophically pure one.

  • Anonymous
    August 07, 2009
    @Patrik Thank you! you are awesome.

  • Anonymous
    August 09, 2009
    The comment has been removed

  • Anonymous
    August 09, 2009
    @Tom: Now, one other of my coworkers told me that the WCF Proxies are interface implementations which cannot be cast to Object. I'll investigate this myself...

  • Anonymous
    August 09, 2009
    Could anyone tell me why ICloneable.Equals(a, b) works? Does this imply any relation between interface and Object? or there is something behind the scene?

  • Anonymous
    August 10, 2009
    @leo:  That's a good question.  Apparently the compiler knows that every object will inherit these static methods, regardless of type, so it allows Object's static methods to apply to explicitly declared interfaces in this context.  After all, any class that implements ICloneable will be an Object.  Clearly it's a rule that applies only to Object, since you can't declare an interface that inherits from a class (not even Object!).

  • Anonymous
    August 10, 2009
    @leo, it works because C# language spec requires it to; 7.3 "Member lookup": "A member lookup of a name N with K type parameters in a type T is processed as follows:

  • If T is a type parameter, then the set is the union of the sets of accessible members named N in each of the types specified as a primary constraint or secondary constraint (§10.1.5) for T, along with the set of accessible members named N in Object.

  • Otherwise, the set consists of all accessible (§3.5) members named N in T, including inherited members and the accessible members named N in Object. If T is a constructed type, the set of members is obtained by substituting type arguments as described in §10.3.2. Members that include an override modifier are excluded from the set." Note how it specifically calls for looking up members in Object, and at the same time makes a distinction between "inherited members" and "members in Object". What is the rationale for this when T is an interface type is a different question, but it is understandable. After all, if you can call instance members of Object on a value of interface type as if they were "inherited" from Object, it would only be logical to extend the same pseudo-inheritance to static members.

  • Anonymous
    August 17, 2009
    Even the authors of C# Language Specification were deceived by this myth. :-) Section 4.2.2 says: "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." Yep. Mads is aware of the error and will correct the spec in the next version. -- Eric Actually, there are some very special value types (like System.TypedReference) that are not convertible to object. Yep. One of these days I'll do a blog post about those very special types. That will at the very least force me to figure out how they work. :-) -- Eric

  • Anonymous
    August 27, 2009
    http://msdn.microsoft.com/tr-tr/library/ms173156(en-us).aspx  this article says "An interface can itself inherit from multiple interfaces.". so, how about that?

  • Anonymous
    September 06, 2009
    Also, this myth is sustained by Jeff Richter's book "CLR via C#, 2nd ed", there is section called "All types are derived from System.Object" in Chapter 4 "Type Fundamentals" of the book.

  • Anonymous
    September 08, 2009
    What about the "null type"? (see C# Language Specification §9.4.4.6, §11.2.7) We decided during the C# 3 design and specification process that having a special type that only contains one value, null, was a bad idea. It seems plausible, and other language specs also do it this way, but ultimately it adds very little value. The null type is quite unusual in that its the only type that doesn't have a CLR type object associated with it. It is conceptually easier to simply have some expressions be typeless, and be OK with that, than to invent an ad-hoc type solely for the purpose of being able to say what the type of the null literal expression is. This language should have been removed from the C# 3 spec. -- Eric

  • Anonymous
    September 13, 2009
    Any idea why following code does not generate compile time error?    public interface ITest
       {
           void PrintMessage();
       }
      // Class does not implement ITest interface.
       public class BaseClass
       {
       }   class Program
       {
           static void Main(string[] args)
           {
               BaseClass bclass = new BaseClass();
               // Why compiler does not generate error here....
               ((ITest)bclass).PrintMessage();
           }
       } First off, of course it generates an error at runtime. But why not at compile time?  Suppose we rewrite your program like this:           BaseClass bclass = GetSomething();
              ((ITest)bclass).PrintMessage(); Do you agree that the compiler cannot produce an error here? Since BaseClass is not sealed, there could be a derived class that implements ITest, and an instance of it could be returned by GetSomething. The compiler is not smart enough to deduce that bclass is always of type BaseClass and never of a more derived class on the codepath that has the cast operator. We do flow analysis, but only for the purposes of determining reachability and definite assignment, not for analyzing the possible runtime types of variables. We could implement such a system, but it would be a lot of work for a small gain. -- Eric

  • Anonymous
    January 20, 2010
    13.2 "The members of class object are not, strictly speaking, members of any iinterface." How about this one? interface MyInterface    {        bool Equals(object obj);        int GetHashCode();        Type GetType();        string ToString();    }    class MyClass : MyInterface    {         // MyInterface is implicitly implemented  because MyClass implicitly derives from type object.   } Isn't it really the case that the members of type object are implicitly members of EVERY interface?

  • Anonymous
    January 27, 2010
    The ability to call System.Object's members is a C# language implementation decision as opposed to the implementation of a language like VB.Net(@Patrick upcast to access System.Object members). Calling ToString() on an instance of an object implementing an interface (Iface) produces IL as such: L_000f: callvirt instance string [mscorlib]System.Object::ToString() Specifying "string ToString();" on the Interface changes the IL such: L_000f: callvirt instance string Iface::ToString() So as the author states the interface isn't inheriting those members from System.Object, it's the compiler that decides to resolve those members to the System.Object class because its a resonable assumption to make that any type that implements Iface is convertable to an object.

  • Anonymous
    January 29, 2010
    I translate this post to chinese: http://www.cnblogs.com/tianfan/archive/2010/01/28/not-everything-derives-from-object.html 我已将此文章翻译成中文: http://www.cnblogs.com/tianfan/archive/2010/01/28/not-everything-derives-from-object.html

  • Anonymous
    July 03, 2012
    gr8!! this is what i was searching for. I had a gr8 confusion about interface type.