What Are The Semantics Of Multiple Implicitly Typed Declarations? Part One


In my earlier series on inferring a unique “best” type from a set of expressions I mentioned that one potential application of such an algorithm is in implicitly typed variables. This led to some good questions and concerns posted in the comments – questions and concerns which echo similar feedback we’ve been receiving from a variety of sources since we released the first technology preview of C# 3.0 last year.

I’d like to run a quick unscientific poll to see what your intuitions and expectations about implicitly typed variables with multiple declarations are. Please leave a comment describing what you think should happen here, and why you think that.

1: local variable declaration var x = 1, y = 2.0; has the same semantics as:
(a) double x = 1, y = 2.0;
(b) int x = 1; double y = 2.0;
(c) object x = 1, y = 2.0;
(d) this should be a compile-time error
(e) something else, please specify

2: local variable declaration var q = 0, r = (short)6; has the same semantics as:
(a) int q = 0; short r = 6;
(b) int q = 0, r = 6;
(c) short q = 0, r = 6;
(d) object q = 0, r = 6;
(e) this should be a compile-time error
(f) something else, please specify

Thanks! Next time I’ll describe some of the pros and cons of each and what our current thinking is in this area.

Comments (21)

  1. Definitely 1b, 2a. The general rule I’m going with here is that

    var a=A, b=B;

    should be exactly equivalent to:

    var a=A;

    var b=B;

    In a sense this is exactly equivalent to the way that other multiple declarations work – if you replace "var" with any real type, this equivalence holds. So I think it passes the "intuitive" test a lot better than any of the other options.

  2. Peter Chamberlin says:

    I’d agree with 1b, 2a also. Though I come to that from interpreting what the coder was thinking of when declaring variables. If lacking type information then I’d presume that things are typed into the most restrictive logical data type given information about the initialisation value. Thus leaving the programmer to explicitly define alternative types, as per the syntax of question 2. I’d also agree that var a=B, b=B is equivalent to var a=A; var b=B.

  3. Orion Adrian says:

    1e, 2e

    Ambiguity, as I’ve found, is the primary cause of subtle bugs in code. Given that it should be illegal to put yourself in to a position like that. Or, it should be a compile-time warning, though given that you can’t get rid of the warning unless you change the type, it might as well be an error.

    "In a sense this is exactly equivalent to the way that other multiple declarations work – if you replace "var" with any real type, this equivalence holds. So I think it passes the "intuitive" test a lot better than any of the other options."

    Except under that logic you could put:

    int a = 2, b = "hello";

    I think intuitive tests have to pass both directions here. Since I can’t translate:

    int a = 2;

    string b = "hello";

    into

    int a = 2, b = "hello";

    or

    string a = 2, b = "hello";

    I shouldn’t, intuitively, be able to translate the var equivalents. Hence, it fails the bidirectional argument.

  4. Marc Brooks says:

    Given that I don’t think you should EVER have been able to do this in C#.

      int x =0, y=1;

    to start with (it’s just-plain ugly and lazy), I’ll got for 1d, 2e.

    But I’m pedantic… if you absolutely must allow the syntax, then the priniciple of least surprise should yield the same behavior as C++ (e.g. all types on the same line share a type) which is option 1b and perhaps a warning that the 2.0 is being truncated… definately a compile time ERROR if it was B was being assigned 2.1 (or any other non-integral value).

    Likewise, 2 should be 2b… simply because in the absence of type information we should dump to int for q, then based on C++ rules r has the same type and is assigned a widened short 6.

  5. I also agree with 1b, 2a. Multiple declarations on a single line is syntactic sugar for the equivalent set of declarations with the same declared type.

  6. Erwyn van der Meer says:

    My answers (without looking at any other comments) would be:

    1 (b)

    2 (a)

    I think there should be no difference between:

    var x = 1, y = 2.0;

    and

    var x = 1;

    var y = 2.0;

    And similarly, no difference between:

    var q = 0, r = (short) 6;

    and

    var q = 0;

    var r = (short) 6;

    Applying the type inference rules: 0 -> int, 1 -> int, 2.0 -> double, (short) 6 -> short leads to my given answers. That said, I could definitely live with flagging both as compilers errors, so answer (d).

  7. 1 = b and 2 = a, if only because this is what I’m used to from every other language I know of that uses type inference.

  8. I’m going to be an oddball and say that it ought to be 1A and 2C.

    I think that under no circumstances should a single instance of the var keyword evaluate to more than one type (this is similar to what Marc Brooks said).

    In the case in which the developer explicitly specifies type information (via casting, etc.) on one of the variables, I believe that the compiler should try to apply this type to all of the declared variables on that line. If there are multiple casts to different types, this should be an error.

    And I definitely agree with the others that have specified that this kind of code should generate warnings at the very least.

  9. 1b 2a

    Yeah it’s a bit annoying at first glance, but I think it works the best.

  10. Iván says:

    1a, 2b: We should be able to do x = y and y = x. A valid reason to use  multiple declarations (the only readon I can think of) is to be sure that we end up with two locals with the same type.

  11. Steve says:

    Definitely 1b, 2a.  Same reasons cited by Stuart and Douglas.

    Plus there’s historical precedent in C, where it is possible to declare variables of different types on one line.

    char ch, *pch, **ppch, rgch[10], (*pfn)(void);

    Whether it was a good idea for C to allow this sort of thing is another question entirely. 🙂

  12. barrkel says:

    1) b

    2) a

  13. Hitesh Pandya says:

    1a, 2b

    I think it would be tedious to debug code where expressions are used to initialise the variables if multiple type can be initialised implicitly in a single statement.  In the interest of mainatiablility, I feel this should be flaged as an error / warning, or the largest data type would be used.  Having said that, it would be a problem if var x = 1, y = 2.0, z="abc"; has to be parsed and hence my answer applies if we are dealing with similar data type.

  14. Mike Dunn says:

    It seems that the choice comes down to how you interpret "var" in those declarations. I can see it meaning either "do type inference separately for each variable declared on this line" or "one and only one type, which is determined by the declarations on this line".

    My first gut feeling was "do inference separately" so I’d go with 1B and 2A

  15. MarkP says:

    1b

    2a

    I think a good reason to avoid options 1a, 2b and 2c is that if the author wants the same type they need only write it instead of var. Therefore treating it the same as multiple var declarations makes more sense to me.

  16. nksingh says:

    both (e).  Var looks like it was put in for two specific purposes:  allowing types to remain anonymous in the LINQ scenario and allowing long genericized instantiations to be written more succinctly (and hopefully readably).  You’d probably wouldn’t have either of these types of var declarations on a shared line.  Since var stands in for a staticly determined type anyway, there’s no real purpose to make it as general as the normal concrete declarations.

  17. I would definitely say it should be 1d & 2e.

    As stated before this is a typical case of subtle bugs waiting to happen. I mean the whole concept of "var" (eventhough I see why it’s important) can cause subtle bugs if the user is not careful, why increase the risk? I’ve seen enough code with type-related bugs even when the type instantiation is explicit.

    Besides there’s absolutely no good reason to not make it an error, I mean if you want two different types write var a = 1; var b = 2.0;

  18. Geert Baeyaert says:

    My first intuition is also 1b, 2a.  

    But since not everybody agrees about what is intuitively correct, I would actually recommend 1d, 2e.  Like some have said before, it’s just a bug waiting to happen.

  19. Marc Brooks says:

    Just to follow up, I polled some veteran VB6 developers who were new to the syntax of C# and thus had no agenda or bias.  I explained the syntax, insured that they weren’t thinking "variant" and the stood back to listen to the discussion.  The universal opinion is that all variables on a single "var" line should be of the same type.  The also called for a compiler error if  the implicit casting was not legal.  So, 6 votes for: 1d, 2b and 5 (overlapped) votes for "and don’t ever do that in code I have to review or you’re getting it back".

  20. Eric Lippert says:

    That’s interesting.  In VB6 if you say

    Dim Curly, Larry, Moe as Stooge

    _of course_ that means that Curly and Larry are Variant, Moe is a Stooge.  

    My guess would be that your VB6 veterans are familiar with the pain that this "gotcha" causes and would like C# to not add a similar gotcha to the language.  

    (.NET versions of VB will treat all three as Stooges.)

  21. Rui Tang says:

    Of course

    1(b)

    2(a)

    If you give only a number to the compiler, e.g. 2.0, the compiler will recognize the const as a double. Except you explictly specify 2.0f to make it a float. The type is clear even though it is under C# 1.0 specification.

    And I think the "var" keyword should better be used with anonymous class. You cannot specify any other class type to the variables except itself and object. We certainlly don’t want the compiler to cast it to an object. Of course we also don’t want compiler to change 0(int) into float, double or object.