It may be helpful to think that 'Object is kind of the "root" of most of the constructs/objects in .NET: including Value Types, such as Integer, and Reference Types such as Classes, Collections, etc.
Interfaces and (unsafe code) Pointer Types, and generic "open" parameter Types, do not inherit from Object.
Eric Lippert eloquently analyzes this in his essay, "Not everything derives from object:" [
^].
We are frequently dealing with objects in .NET that have complex levels of inheritance; consider a WinForms Button:
System.Object
System.MarshalByRefObject
System.ComponentModel.Component
System.Windows.Forms.Control
System.Windows.Forms.ButtonBase
System.Windows.For System.Windows.Forms.Button
You can think of each of the "ancestors" of Button as exposing facilities that can be accessed by any instance of Button, such as Methods, Fields, etc.
It makes sense to say: "a Button is a child of all its ancestors," just as makes it sense to say that you are a child of your ancestors.
It doesn't make sense to say your grandfather is a child of you, and it doesn't make sense to say that a System.Windows.Forms.ButtonBase is a child of a Button.
So, there's a one-way arrow in an object's inheritance tree.
It's logical that I can take any instance of a Button, and deal with it as any of its object ancestors: you can think of this as down-casting, and while the object down-cast may cease to
expose the unique facilities provided by its higher-level Type, it doesn't cease to
be an instance of the higher-level Type.
An example may help illustrate this: every .NET Control exposes a 'Tag field into which any object can be placed.
Let's assume you have a WinForm Project, and an instance of a TreeView, 'treeView1, placed on 'Form1. At design time you add some root Nodes to the TreeView.
If you add a Class, like this:
public class TreeNodeTag
{
public int ID { get; set; }
public string Comments { get; set; }
public TreeNodeTag(int id, string comments)
{
ID = id;
Comments = comments;
}
}
To your project, and then define some variables, and an EventHandler for the Form Load Event like this:
private TreeNode currentNode;
private TreeNodeTag currentNodeTag;
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i < treeView1.Nodes.Count; i++)
{
currentNode = treeView1.Nodes[i];
currentNodeTag = new TreeNodeTag(i, "comment " + i.ToString());
currentNode.Tag = currentNodeTag;
}
}
Now, every root-level TreeNode has an instance of a 'TreeNodeTag in its 'Tag Property, but this instance is down-cast into Type Object.
So, what if you want to access the TreeNodeTag as TreeNodeTag, not as Object: for example, assume you've defined an 'AfterSelect EventHandler for the TreeView:
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
currentNode = e.Node;
currentNodeTag = currentNode.Tag as TreeNodeTag;
if(currentNodeTag == null)
{
MessageBox.Show(string.Format("Node: {0} does not have a Custom Tag Object", currentNode.Text));
return;
}
MessageBox.Show(string.Format("Node: {0} ID: {1} Comments: {2}", currentNode.Text, currentNodeTag.ID.ToString(), currentNodeTag.Comments));
}
As you can see in this code, an instance of the TreeNodeTag Class is stored in the Tag Property of the TreeView's Nodes as Type 'Object, but it is not "stripped" of its information.
When we retrieve the TreeNode.Tag, we can cast it back to its higher-level Type, and, all the information stored in it is accessible.
While this may seem to "violate" the idea of the one-way nature of object inheritance, it really means that the information in a higher-level object can be
restricted from use, or view by down-casting it to an ancestral form.
Hope this meandering essay is useful :) If not, I'll remove it.