Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do we have to use typeof, instead of just using the type?

Tags:

c#

When trying to assign a type to a property of type System.Type, why can't we do this?

foo.NetType = bool;

The compiler produces this warning:

"Expression expected."

The way to solve it is by doing this:

foo.NetType = typeof(bool);

My question is why can't we use the first approach? Isn't the compiler smart enough to figure out what we're trying to accomplish here? Is there some reason why we have to go with the second approach (typeof)?

like image 590
Bob Horn Avatar asked Mar 20 '13 21:03

Bob Horn


People also ask

What is difference between type and typeof?

Both of them produce the same information. The difference lies that from where the information is got. typeOf is used to get the type based on class. It will give an error if typeOf is used with the object.

What is the use of typeof ()?

The TypeOf function is an important tool when dealing with complex code. It allows a programmer to quickly check a variable's data type—or whether it's “undefined” or “null”—without going through the code line by line! Additionally, the TypeOf function can also check whether an operand is an object or not.

Should I use typeof or ===?

If typeof(typeof(x)) is always string, no matter what x actually is, then == should be sufficient and === unnecessary. The most efficient reason for not using typeof and rather the === operator would be for type coercion (interpretation) between browsers.

Why do we use typeof in C#?

The C# typeof operator ( GetType operator in Visual Basic) is used to get a Type object representing String. From this Type object, the GetMethod method is used to get a MethodInfo representing the String. Substring overload that takes a starting location and a length.


1 Answers

Good question -- insofar as it is an interesting question in language design. This is maybe not an ideal question for this site, as it is not about specific, actual code.

It would be perfectly feasible to design a language in which a type name may be used as an expression without an explicit typeof operator.

Doing so would require a small number of extra rules and clarifications to be added to the language. For example, suppose we had:

enum Color { Red }
class Square 
{
    Color Color { get; set; }
    void M()
    {
        Type t = Color.GetType(); 

In C# today this unambiguously means invoke the getter for property Color and call GetType on the result. (See the specification's "Color Color rule" section for an explanation of why.) In your proposed world there are three things this could mean:

  • Invoke the getter for Color, call GetType on the result, assign the Type object for Color to t.
  • Color results in a Type object for the type Color. Call GetType on that type, and assign the Type object for System.Type to t.
  • Color refers to the type Color, GetType refers to the non-static method, and this is an error because this is a static call to a non-static member.

You'd want to clarify the specification so that it was clear when Color in an expression meant the type and when it meant make a type object. So the proposed feature adds a small amount of ambiguity that must be dealt with, but it's totally doable. The language designers could come up with reasonable rules.

A language which allows a language element that is normally part of the compile-time analysis to instead be interpreted as code that creates an object that can be manipulated by code in the same program is called a homoiconic language. C# was a very nonhomoiconic language before C# 3; expression trees made C# much more homoiconic. C# 3 allow lambdas to be treated as program elements that are then used to generate methods that perform the action of the lambda body, but it also supports a homoiconic transformation from the lambda into an object that represents the body of the lambda.

One of the most homoiconic languages is Lisp; Lisp manipulates lists and any Lisp program can itself be though of as a list; it can be quoted and manipulated at runtime as objects rather than as code. So once again we see here the truth of the old joke about language design: every language designer eventually re-invents Lisp, badly.

I digress. Your question then is essentially: should C# be more or less homoiconic with respect to type names? Should a type name have one meaning -- a compile-time directive -- in contexts like

Foo x = new Foo();
object o = new List<Foo>[] {};
Foo.StaticMethod();

and have a very different meaning -- as the construction of an object that can be inspected at runtime in other contexts:

object t = Foo; // Assign a Type object to t.

?

Though it would certainly be possible to design a language like that, I don't much like it. Without an operator in there clearly calling out "hey, we are using what is normally a compile-time element in a homoiconic manner", it's potentially confusing. Before C# 3, there were no other homoiconic language features and so it would seem a bit strange to have the only one be that types could be used as both types or expressions that result in a Type object. And it does seem quite unfortunate that in some expressions it is unclear whether a particular simple name means "I'm using this type at compile time" or "I want to make an object".

Having an explicit typeof operator mitigates all these concerns; it is unambiguous when the type is being used homoiconically and very clear to the reader.

To address a very specific point about your question:

When trying to assign a type to a property of type System.Type...

C# does not generally speaking have special rules that apply only in assignments. The meaning of an expression is usually determined without appealing to the context in which the expression is being used. When we say:

x = y;

We don't generally say "Oh, I know that y is being assigned to x and x is of type X so I'm going to use that information in the analysis of y". Rather, the analysis goes from inside to outside: we work out what y means, and then decide whether or not it is compatible with X.

The exception to this rule is of course lambdas; lambdas do take into account their "target" type because the target type can be used to infer the types of an implicitly typed lambda. Getting these rules right was very complicated.

More generally, it's a bad idea to make assignment expressions special. There are lots of ways that values get assigned to variables:

M(x); // x is assigned to a formal
q = new [] { x }; // x is assigned to the first element of an array
y = new Y() { X = x }; // x is assigned to a property of Y.

You want the rules for all those assignments to be consistent; if an expression means "I'm a type object" then it should mean that in every context in which that expression can appear, not just as the right hand side of an assignment.

like image 106
Eric Lippert Avatar answered Sep 18 '22 05:09

Eric Lippert