The underlying issue is not Session or Asp.Net specific, it is a fundamental issue in how collections in statically typed languages work. The Session
object is designed to let you store objects of any type. This means the static type of the stored items must be Object
. So Session
is equivalent to a Dictionary<string, Object>
. Since the compiler can only know that the type of the item is Object
, you have to explicitly cast to a more specific type in order to perform any operation (and that cast may of course fail at runtime if you get the wrong type.)
Now you ask why the compiler is not smart enough to recognize that:
Session["name"] = "abc";
means that the entry with key "name" will be of type string
. This cannot work because the name of the key is an expression with a value which may be unknown at compile time. Eg.
Session[Datetime.Now.ToString()] = "abc";
There is no way the compiler can figure this out in the general case, even if it is theoretically possible in some very specific cases.
To explain a bit more generally: You have to distinguish between the type of a value at runtime, and then the type that the compiler is able to determine at compile time.
A value always have a specific type at runtime, and this type is (as you suggest) stored together with the value, so you can always inspect the actual type of a value with the GetType()
method.
But in a statically typed language, the type checking happens at compile time, and at that stage the compiler is not always able to know the actual type of all values. Take this example:
object x = "abs";
int l = x.Length; // !Compile error
At runtime, the type of the value in x is String
, but the compiler have to rely on the declared type of x with is Object
. And since Object
does not have the property Length
, you get a compile error.
Now you might think that compiler should be smart enough to realize that x is actually a String
in this case, but this will not work in the general case, since x might be assigned say an integer in a different part of the program, so the actual type might change through the running of the program. The only thing the compiler can guarantee is that the type of x will be Object or a subtype of Object, because that is the declared type.
In this particular case you know more than the compiler, because you realize that x is a String
, even though the declared type is less specific. This is where we can use a cast: When we know the runtime type better than the compiler. A cast does not change the type of a value, rather it just informs the compiler to assume a certain more specific type. So you can write:
int l = ((string)x).Length;
A cast will of course fail at runtime in case the actual type of the value in x is not string.
Best Answer
This usually is related to reflection, since reflection is the only way to look at attributes or annotations (depending on the language). So it might be possible for LISP but not for C++.
Also, annotations are only a series of name/values that are tagged to a specific class, method, field, etc.
So inside the meta-object that stores information about a class (ex. it's methods, fields, etc.), one needs to add another map (or list) that contains information about all the attributes an class or method contains.
Obviously, if you don't know what a meta-object is, you should study meta-programming a little, particularly CLOS (Common Lisp Object System), that is the mother of all languages implementing metaprogramming.