views:

302

answers:

9

I am asking this because it seems like using Object seems to be an easy way out to solve certain problems, like "I don't have a specific type, so use Object", etc.

Also the reason this made me curious is because a colleague of mine told me that if .NET was a true object-oriented platform then it wouldn't have to have a catch all type like Object.

So if .NET didn't have the Object type, what would be the alternative ways to solve the occuring problems to make it function just the same?

Also just to note, this is not to bash .NET, as I use it daily in my work. Just want to know more about it.

EDIT: Another note I remembered is because the type Object exists, its effect ripples throughout the whole .NET. Like IEnumerable exists but also IEnumerable<T>. And in many situations you have to implement both the generic and non-generic version of things, etc.

+2  A: 

In a strongly-typed framework, Objects have to start somewhere.

The type object is not there to provide an "easy" catch-all casting option, or to force you to cast down to the lowest common denominator. It is there to provide the absolute most general case for an object- a basis for reference comparison and a handy ToString() method, among a few others.

Dave Swersky
And the GetType() method, which is inherited from System.Object because we know that all Objects have a Type.
FacticiusVir
Thanks so you mean Object helps because it can provide ToString method for everything? Couldn't the compiler do this behind the covers so it would looks like every type has it?
Joan Venge
@Joan: I like the fact that System.Object explicitly implements the basics, including GetType() (as FacticiusVir mentioned) and ToString(). I wouldn't want the compiler working some mystical voodoo under the covers that I couldn't see without Reflector.
Dave Swersky
What if instead of Object.ToString, all types implemented an interface that included a basic implementation for each type? Or a type that's used inside each type where ToString, GetType methods mapped? Like MyType.ToString (which actually calls MyType.Base.ToString)?
Joan Venge
Interfaces can't provide implementation. ToString() is implemented by Object and, by default, spits out the string representation of the object's type. You can override this in your own classes.
Dave Swersky
Why do objects have to start somewhere? In C++ they don't have to.
Henk Holterman
Seems like you contradict yourself: "is not there to make things easy" and "It is there to provide [...] and a handy ToString() method"
Henk Holterman
@Henk: By "easy" I mean a catch-all casting technique.
Dave Swersky
Objects having to start somewhere to be OO isn't right. -1
Matt Briggs
+3  A: 

I would say that the problem that is solved by Object is not "I don't have a specific type, so use Object", but rather "I don't really care what type this is; all I need to know it that it is an Object"

Fredrik Mörk
+3  A: 

It is a convenience, not only for use as a 'generic' type but also for reflection, garbage collection etc.

Other languages (C++) can do without, but I would hesitate to say that makes those languages more OOP.

Henk Holterman
Well, I would say that there is an equivalent to `Object` in C++: `void*`
Dr. Wily's Apprentice
@Willy: No, nobody inherits from `void*`. Different solution to a different (pointer) problem.
Henk Holterman
The OP's question didn't mention inheritance specifically, nor did this answer. Perhaps I shouldn't have said "equivalent", but I think that in many situations the C++ `void*` concept solves the same problems as the .NET `Object` data type. If I wanted to build some kind of container class in C++, but I didn't know exactly what type of objects would be put into my container class, I could use `void*`. Alternatively, I could also use C++ templates, but .NET's only equivalent to that is Generics, which was only introduced in version 2.0.
Dr. Wily's Apprentice
+1  A: 

Yes object type can be misused, but it provides seminal functionality to the world of .NET (above all IMO is GetType()). So if there is a problem, it is not with having the type of object.

Alternatives are many, including Generics and OOP practices such as interfaces...

Aliostad
+2  A: 

Remember that inheritance denotes an "is-a" relationship. Every class in .NET "is a(n)" object. They all have a ToString method. They all have a type, which you access through GetType. This sort of relationship and the resultant sharing of functionality is the basis of object-oriented programming.

Jake
Thanks so you mean Object helps because it can provide ToString method for everything? Couldn't the compiler do this behind the covers so it would looks like every type has it?
Joan Venge
@Joan-Venge That isn't the point of `Object`, but I'm pointing out that all classes do and should share a small set of attributes with one another. These commonalities are expressed through the `Object` type. Many of the earlier uses of 'Object' could probably be handled now by generics, but this still doesn't go against OO. If all of your classes are more specific versions of a single base class, shouldn't you implement that base class to consolidate functionality and provide a contract for other pieces of code?
Jake
@Jake, yeah that makes sense.
Joan Venge
+1  A: 

If the object derivation hierarchy didn't have a single unified head, then it would be impossible to test for equality between any two arbitrary...uh...things without resorting to dynamic typing.

Other than that, I suspect the functionality of object could have been handled just about as well by separate interfaces (IEqualable and IConvertableToString). On the other hand, object's virtual methods are pretty handy sometimes, especially ToString, which can be used by an IDE or debugger when displaying program state. It's really a pragmatic design.

Jeffrey L Whitledge
Thanks, I was thinking about IEqualable and IConvertableToString too.
Joan Venge
+3  A: 

Your friend probably works with a dynamic language (like ruby or python), right?

There are a great many people who think that strongly typed languages should be referred to as "class oriented" rather then "object oriented", because all possible behaviors and polymorphism needs to be done up front in the class definition. When your method accepts anything, any any checks are done based off of the objects capabilities rather then its class, you could say that is a more object oriented approach.

I am sort of conflicted on this argument. There is this boneheaded belief in programming circles that OO is unequivocally Good, no matter what the problem or requirements. Because of that, people tend to try to win arguments by saying "such and such isn't object oriented", and since object oriented is a synonym for good, they win. Even though I think static languages are a pain to work with, I think calling them not OO is a disingenuous way to make your point. On the other hand, anything that will make a programmer reach outside his comfort zone and learn a new way to do something (if for no other reason then to win an argument) can't be totally bad. As I said, conflicted. :)

Matt Briggs
He is actually a C++ programmer but I think he has a bias/favor on python.
Joan Venge
Yeah, that is probably where he is getting that from. As him if it is because he thinks C# is class oriented rather then object oriented.
Matt Briggs
+1  A: 

I'm by no means particularly knowledgeable on this subject, but from my perspective it was useful to allow people to build polymorphic components without having prior knowledge of how those components would be consumed. What do I mean by that? Let me try to explain.

Let's take a simple example with the .NET framework's ArrayList class. This was part of the original framework, before Generics were introduced. The authors of the ArrayList class were trying to provide a useful dynamic list implementation, but they had no way of knowing what kinds of objects would be inserted into the list. They used the Object type to represent the items in the list because it would allow any type of class to be added to the list. For example:

        ArrayList people = new ArrayList();
        people.Add(new Doctor());
        people.Add(new Lawyer());
        people.Add(new PetDetective());
        people.Add(new Ferrari()); // Yikes!

        // ...

        for (int i = 0; i < people.Count; i++)
        {
            object person = people[0];
            // ...
        }

Now, if this were your own application and you knew that your Doctor, Lawyer, and PetDetective classes all derived from a common Person base class, then you could, in theory, build your own linked list implementation based on the Person class rather than the Object class. However, that's a lot of extra work for very little benefit when you already have a built and tested ArrayList class. If you really want to make it specific to your Person base class, then you could always create a wrapper class for ArrayList that only accepts Person-derived objects.

In C++, you could do essentially the same thing using the "void pointer" data type (void*). However, C++ also supported templates (very similar to generics), which made it much easier to build a useful component without knowing the details of what other classes it would be used with. Since C# did not initally support generics, using the Object type was really the only way to build general polymorphic components for other people to use.

Dr. Wily's Apprentice
So the reason to have the type Object had to be added simply because Generics wasn't in version 1? If it was, we wouldn't need the type Object?
Joan Venge
I really don't know if that was a driving reason; I'm certainly not in a position to say what Microsoft's motivations were at that time. I am only trying to illustrate a possible reason for its existence, though. As others have pointed out, it also provides a base implementation for Equals, GetHashCode, and ToString, but I agree with your point that an alternative approach to that could have been to have a special interface that defines those methods and to have the compiler implicitly make all classes implement that interface.
Dr. Wily's Apprentice
+1  A: 

The idea that the concept of System.Object could be replaced by interfaces that, as a rule, all classes implement has been mentioned a few times on this question. While I think the idea is valid, in the end I would say that it doesn't buy you anything. Even if such interfaces existed, the question would still remain of how the compiler would actually ensure that classes implemented those interfaces.

Below is some example code where I try to explore a hypothetical situation where there is no System.Object type and how the implementation under the hood as well as usage of those interfaces might look.

// let's start off by defining interfaces to describe the various methods that are currently available from the System.Object class

public interface IEquatable
{
    bool Equals(IEquatable other);
}

public interface IHashCodeGenerator
{
    int GetHashCode();
}

public interface ITypeIdentifiable
{
    Type GetType();
}

public interface IConvertibleToString
{
    string ToString();
}

// This guy throws a wrench into things, because we can't privately (or "protectedly") implement an interface.
// This is discussed further below on the MyClass.MemberwiseClone method.
public interface IMemberwiseCloneable
{
}

// This class simply encapsulates similar functionality found within the System.Object class
public static class ClrInternals
{
    [MethodImpl(MethodImplOptions.InternalCall)]
    internal static extern bool Equals(IEquatable objA, IEquatable objB);

    [MethodImpl(MethodImplOptions.InternalCall)]
    internal static extern int GetHashCode(IHashCodeGenerator hashGenerator);

    [MethodImpl(MethodImplOptions.InternalCall)]
    internal static extern Type GetType(ITypeIdentifiable typedInstance);

    [MethodImpl(MethodImplOptions.InternalCall)]
    internal static extern IMemberwiseCloneable MemberwiseClone(IMemberwiseCloneable original);
}

// let's say that as a rule the compiler implicitly makes all classes implement these interfaces
class MyClassExampleA : IEquatable, IHashCodeGenerator, ITypeIdentifiable, IConvertibleToString, IMemberwiseCloneable
{
    // The compiler also implicitly makes all classes implement the interfaces with the following code (unless otherwise specified)

    #region IEquatable Members

    public bool Equals(IEquatable other)
    {
        // let's suppose that this is equivalent to the current implementation of Object.Equals
        return ClrInternals.Equals(this, other);
    }

    #endregion

    #region IHashCodeGenerator Members

    public int GetHashCode()
    {
        // let's suppose that this is equivalent to the current implementation of Object.GetHashCode
        return ClrInternals.GetHashCode(this);
    }

    #endregion

    #region ITypeIdentifiable Members

    public Type GetType()
    {
        // let's suppose that this is equivalent to the current implementation of Object.GetType
        return ClrInternals.GetType(this);
    }

    #endregion

    #region IConvertibleToString Members

    public string ToString()
    {
        // let's suppose that this is equivalent to the current implementation of Object.ToString
        return this.GetType().ToString();
    }

    #endregion

    // this one is perhaps a little goofy, since it doesn't satisfy any interface
    // In order to be equivalent to the current Object.MemberwiseClone implementation, I've made this protected,
    // but we cannot have a protected method that implements an interface, so this throws a wrench into things.
    protected MyClassExampleA MemberwiseClone()
    {
        // let's suppose that this is equivalent ot the current implementation of Object.MemberwiseClone
        return (MyClassExampleA)ClrInternals.MemberwiseClone(this);
    }

    // ** All of the above code is just a representation of the implicit semantics that the compiler/CLR applies to a class.  Perhaps this code is not actually generated by the compiler for each class (that would be a lot of duplication!), but rather the CLR might handle this logic internally
}


// Ok, so now I'm implementing a general Stack class
public class Stack
{
    // what type should I use for the parameter?
    // I have five different interfaces to choose from that I know all classes implement, but which one should I pick?
    public void Push(type??? item)
    {
        // ...
    }

    // what type should I use for the return type?
    // I have five interfaces to choose from, but if I return one,
    // then my caller can't utilize the methods defined in the other interfaces without casting.
    // I know all classes implement all five interfaces, but is it possible that my Stack might also contain non-class objects that don't implement all interfaces?  In that case it might be dangerous for the caller to cast the return value from one interface to another.
    public type??? Pop()
    {
        // ...
    }

    // In C++ I could have used void* or defined the Stack class as a template
}

// moving on...
class StackUtilizer
{
    // here I try to utilize the Stack class
    public void UseStack(Stack stack)
    {
        // what type should I use for the variable to hold the result of the Stack.Pop method?
        type??? item = stack.Pop();

        // if I use IEquatable
        IEquatable item1 = stack.Pop();

        IEquatable item2 = stack.Pop();

        item1.Equals(item2); // then I can do this

        Type itemType = item1.GetType(); // but I can't do this

        string s = item1.ToString(); // nor can I do this

        // Ok, this calls for another interface that composes all of these other interfaces into one
    }
}


// let's define a single interface that pulls all of these other interfaces together
public interface IObject : IEquatable, IHashCodeGenerator, ITypeIdentifiable, IConvertibleToString, IMemberwiseCloneable
{
    // no need to define any methods on this interface.  The purpose of this interface is merely to consolidate all of these other basic interfaces together.
}

// now we change the compiler rule to say that all classes implicitly implement the IObject interface
class MyClassExampleB : IObject
{
    // ... <refer to MyClassExampleA for the implicit implementation of the interfaces>
}

// now let's try implementing that Stack class again
public class Stack
{
    // I know that all classes implement the IObject interface, so it is an acceptable type to use as a parameter
    public void Push(IObject item)
    {
        // ...
    }

    // again, since all classes implement IObject, I can use it as the return type
    public IObject Pop()
    {
        // ...
        throw new NotImplementedException("This is an example.  The implementation of this method is irrelevant.");
    }
}

class StackUtilizer
{
    // here I try to utilize the Stack class
    public void UseStack(Stack stack)
    {
        // now I can just use IObject for my variables holding the return value of the Stack.Pop method
        IObject item = stack.Pop();

        // if I use IObject
        IObject item1 = stack.Pop();

        IObject item2 = stack.Pop();

        item1.Equals(item2); // then I can do this

        Type itemType = item1.GetType(); // and I can do this

        string s = item1.ToString(); // and I can do this
    }
}

So, in the end we still have an IObject interface, similar to the current System.Object class. The open question is how the compiler/CLR would handle enforcing our rule that all classes implement the IObject interface. I can think of three possible approaches:

  1. The compiler generates the implicit interface implementation for each class, which would cause a lot of duplication.
  2. The CLR would handle those interface implementations in a special way that would not require the compiler to actually generate code for each class.
  3. We define a base class, let's call it Object (starting to sound familiar?), that implements the IObject interface and we change the rule to say that all classes implicitly inherit from Object (this is exactly what we have today but without the interfaces).
Dr. Wily's Apprentice
`class Stack { public void Push(IStackable item) {...}...} interface IStackable {} class IntStackItem : IStackable { public int value; } // Ta-da!` Yeah, that’s pretty terrible. Nevermind.
Jeffrey L Whitledge
Ha ha. Actually, that's a pretty clever approach, though, given the situation. Thanks!
Dr. Wily's Apprentice
Thinking on that a bit further, that would be a good reason to allow consumers to declare that a class/struct/etc. implements an interface, even if they don't have control over the declaration. What I mean is, currently you have to have control over the declaration of a class in order to say that it implements an interface. It might be useful to be able to say that a class implements an interface (if it satisfies the requirements of that interface), even if that class's original author was not aware of that interface.
Dr. Wily's Apprentice