13

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)?

Bob Horn
  • 33,387
  • 34
  • 113
  • 219
  • 1
    Eric Lippert is the only SO participant I'm aware of who could give an accurate answer to this question (*why* the C# language is specified the way it is). – Eric J. Mar 20 '13 at 21:59
  • 1
    Jon Skeet could as well, I'm sure. – Tim Mar 20 '13 at 22:00
  • @Tim: Was Jon involved in creating the C# spec? He's not on the language team (works at Google). – Eric J. Mar 20 '13 at 22:00
  • 1
    @EricJ. - No, but he's extremely knowledgeable about it. Just because he wasn't involved in creating the spec doesn't mean he wouldn't know why certain decisions were made. – Tim Mar 20 '13 at 22:01
  • 2
    @EricJ.: I assume the answer is simple; this feature would add inconsistency to well known operators for no added benefit. – Ed S. Mar 20 '13 at 22:06
  • 1
    But I actually don't see too much difficulty with syntactically incorporating such a feature into the language. I couldn't off-the-top-of-my-head think of any ambiguous uses (if the compiler is allowed to check if the lhs of the assignment is of type `System.Type`) But it would be unlike other syntax and wouldn't fit well, I think. – Matthew Watson Mar 20 '13 at 22:06
  • @MatthewWatson: But why would you do it? How is it useful? It makes the code less clear and more confusing for no good reason. – Ed S. Mar 20 '13 at 22:07
  • I agree, but I was really just remarking that there's no technical reason, more a practical one. – Matthew Watson Mar 20 '13 at 22:08
  • @MatthewWatson: Sure, agreed – Ed S. Mar 20 '13 at 22:09

3 Answers3

21

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.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
6

Sorry, I misunderstood your goal at first. However, you're still a bit confused.

You're attempting to assign an instance of a Type object to a property. bool is not an instance of the Type class, it is its own type. I appreciate that the terminology is a bit confusing, but they are two different things. That's why it doesn't work.

Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • 2
    Why is this the way to solve it: `foo.NetType == typeof(bool);` I'm trying to assign a type to a property, and `==` is for equality, not assignment. `foo.NetType = typeof(bool);` works just fine in my code, so I don't understand what's wrong about that. – Bob Horn Mar 20 '13 at 22:12
  • @BobHorn: Sorry about the equality thing, my mistake. However, you're still confused. You're attempting to assign an `instance` of a `Type` object to a property. `bool` is not an instance of the `Type` class. They are two different things. – Ed S. Mar 20 '13 at 22:33
  • I get why it doesn't work. Folks keep thinking I don't get it. I do. I'm asking why it couldn't be changed to make it work. – Bob Horn Mar 20 '13 at 22:41
  • @BobHorn: I answered you. It doesn't make sense, that's why. They're two different things. Of course the compiler team could make a special case for this scenario and implement it, it's not a technical impossibility. But why should they? It adds complexity for no good reason other than two save you a few keystrokes, and more importantly, *it simply doesn't make sense* unless you know that it is a special case of the equality operator. Read Eric Lippert's answer here: http://stackoverflow.com/questions/2806894/why-c-sharp-doesnt-implement-indexed-properties – Ed S. Mar 20 '13 at 22:52
  • I get what you're saying, and I just gave you +1 for the tip. `typeof` returns an instance of a type, and that's what the property holds. – Bob Horn Mar 20 '13 at 22:59
  • Sorry, bro, but Eric's answer is exactly what I was looking for. Need to change the accepted answer. – Bob Horn Mar 21 '13 at 12:54
  • @BobHorn: I have no expectations that my response will be better than Eric Lippert's essay :D. He is a language designer after all... specifically, C#. Don't worry about it, the voting system exists for reasons other than boosting my ego. – Ed S. Mar 21 '13 at 17:05
2

So here's a SWAG:

If I have a class:

public class Foo
{
    public static int Bar = 1;
}

And I have some code like:

var foo = Foo;

You might say "ok, that definitely means the type"

Now if I have something like:

public class Bar
{
    public void Foo ()
    {
          var hmm = Foo;
    }
}

What do I mean? The type Foo? The method Foo? The namespace (if it existed) Foo?

By wrapping it in typeof, we make it explicit what we want. Also, it generates specific IL, but I'm assuming your question implicitly means "Why isn't the compiler smarter?"

JerKimball
  • 16,584
  • 3
  • 43
  • 55
  • Additional random thought: pulling a type is not always a trivial operation; making it an explicit operation makes intent clear and also prevents accidental side effects from dependent type loads, etc. – JerKimball Mar 20 '13 at 23:22
  • +1. I like the "explicit" reference. However, if we didn't use var, I don't see how `Foo foo = Foo` could be taken another way. – Bob Horn Mar 21 '13 at 00:20
  • @BobHorn I see your point; I mean, the short-and-easy answer is EdS's "They are just different things", but was trying to come at it from the mind of a compiler developer. – JerKimball Mar 21 '13 at 00:29