C# newbie question here. The following code (taken from the book "C# From Novice to Professional" by Christian Gross, Apress) gives an error:
worksheet.Add("C3", CellFactories.DoAdd(worksheet["A2"], worksheet["B1"]));
The reason is that the method DoAdd()
does not accept the given arguments.
public static Func<object> DoAdd(Func<object> cell1, Func<object> cell2) {...}
VS claims that both args in the method call above are of type object
whereas the method accepts only Func<object>
. But the value of both worksheet elements is of type Func<object>
:
worksheet.Add("A2", CellFactories.Static(10.0));
where this Static
method just returns the given value:
public static Func<object> Static(object value) { return () => value; }
// return type= Func<object>
When I cast worksheet["A2"]
as Func<object>
, the code does work.
But there is something I don't understand. The type of the object instance is Func<object>
. I have used the GetType()
method to see proof of this, and compare the object types of the original elements to that of the cast object (which IS accepted):
Console.Writeline(worksheet["A2"].GetType());
// now cast to the correct type (why can't it do that implicitly, btw?)
Funk1 = worksheet["A2"] as Func<object>;
Console.Writeline(Funk1.GetType());
.. and they are ALL identical! (Type = System.Func'1[System.Object]
)
And even when I use the .Equals()
method to compare both types, it returns true
.
Yet, VS sees the first object instance as type object
in the method call. Why? Why does the called method 'see' the argument as a different type than the GetType() returns?
(and if so, what good is the GetType()
method?)
Thanks a lot for your advice/comments! (It's kinda hard to learn the language if the book examples give an error and you don't see the reason - hence, got the vague impression that something is wrong either with GetType()
or VS.)
You need to understand the difference between dynamic typing and static typing. The indexer for your
worksheet
object most likely has a static type ofobject
.Because all objects in C# inherit from type
object
, the object reference stored in a cell can be a reference to any object.That is, because a delegate (such as
Func<T>
) is anobject
, it can be stored in anobject
reference:And the compiler can figure this all out, because it understands implicitly that a derived class can be stored in a reference to a base class.
What the compiler cannot do automatically, is determine what the dynamic type, or run time type of an object is.
The compiler doesn't know that the
object
stored ino
is actually of typeFunc<object>
. It's not supposed to keep track of this. This is information that must be checked at run time.The above line of code compiles into something that behaves similarly to this:
In this way, any cast (conversion from one type to another) can be checked at run time to make sure it's valid and safe.
Others have given accurate and detailed answers, but here I will try to explain in simple language.
When you write
worksheet["A2"]
you really are calling a member function ofworksheet
worksheet
has a member function named[]
that accepts astring
and returns anobject
The signature of the member function
[]
looks likeobject this[string id]
So the function
worksheet["A2"]
returns something that is anobject
. It could be anint
or astring
or many other things. All the compiler knows is that it will be anobject
.In the example, you have it returning a
Func<object>
. This is fine, becauseFunc<object>
is anobject
. However, you then pass the result of that function in as a parameter to another function.The problem here is that the compiler only knows that
worksheet["A2"]
returns anobject
. That is as specific as the compiler can be. So the compiler sees thatworksheet["A2"]
is an object, and you are trying to pass the object to a function that does not acceptobject
as a parameter.So here you have to tell the compiler "hey dummy, that's a
Func<object>
" by casting the returned object to the correct type.can be re-written as
Now the compiler knows that, even though the
[]
function returns anobject
, it can treat it like aFunc<object>
.side note: You're probably doing too much on one line. That may be hard for people to read in the future.
The compiler only knows that
worksheet[]
returns an object. The compiler can not callGetType()
on it at compile time.There are quite a few uses and abuses of the
GetType()
method, but that is an entirely different discussion. ;)In summary, the compiler does not assume anything about types. This is a good thing because you get a compile time error when you try to fit this square peg into a round hole. If the compiler did not complain, this error would surface at run-time, which means you would probably need a unit test to detect the problem.
You can get around this problem by telling the compiler "I know for a fact that this thing is a round peg, trust me." and then it will compile. If you lie to the compiler, you will get a run-time error when that code is executed.
This is called "static typing". The opposing philosophy is called "dynamic typing" where type checks are done at run-time. Static vs dynamic is a lengthy debate and you should probably research it on your own if you're interested.
Yes, but the declared type is
object
. The compiler can't know that the actual runtime type will beFunc<object>
, so an explicit cast is necessary.