use of var

  • Thread starter Thread starter Peter
  • Start date Start date
That said, I do see a difference between:

     Control control = new TextBox();

and

     var control = new TextBox();

The question is this: given that "var" is confined to locals only, is
there any case you could think of where it would actually benefit to
use some supertype instead of an exact type in a declaration? I can
understand the reasoning for using supertypes (and interfaces) in
public members - to minimize exposure of implementation details. But
in case of locals, the whole body of the method is one big
"implementation detail", so how does it help?

The only case I can think of where you'd actually need the declaration
above is when some method takes an "out Control" or "ref Control"
argument. In which case I agree that the type should be explicit in
the declaration, because at that point it matters. But that's a very
special, and arguably rare case.
There are zero code execution performance benefits.  And I feel that using  
"var" can only take _away_ understanding of the code, never add to it.

Readability is not just about understanding. For complicated nested
generics (Dictionary<Tuple<Func<List>>>, ...> etc), repeating the type
twice in declaration makes eyes bleed twice as hard for no gain
whatsoever. On the whole, the net effect in those cases is negative.
 
IMHO it makes the code less readable and more cumbersome to
understand. For example i nyour example above, by seenig the line you
do not know the type of user

Why would you care about the specific type of the user, rather than
what you can do with the value that you have?

In those few rare cases when you actually do care about the type, your
IDE will happily display the inferred type with a single mouse hover.
it's also more difficult to get to the defiintion of the type.

Not at all. Again, when using a decent IDE, it shouldn't matter at
all, and, indeed, in VS it does not - "go to definition" works just as
well.
 
Of course. But not everyone is going to follow the simplistic way you
have presented.  Besides, over time var form can become "var
somethingElse" while "Form somethingElse" is still good.

"Form somethingElse" is not good, because the name is not descriptive,
and that's a problem in and of itself. Using or not using "var"
doesn't change it. On the other hand, using a proper descriptive name
should make it clear enough that, within the context that the variable
is used, it is clearly at least a Form (and possibly some subclass).
No, its not. Second example assumes the reader to know that Control is
a type and is implied by that statement. You assume that coder did
what you as a reader think they should do by naming the variable
"control".

It doesn't do that. It says that variable "control" holds a reference
to a control - this does not necessarily imply that there is a type
Control. It does, however, imply that you should be able to do with
"control" any operations that make sense for a control, whatever the
meaning of "control" is in this context.

It often happens that such naming coincides with type names when good
descriptive names are used for both types and variables - precisely
because they are descriptive. However, this is a side-effect, not a
hard rule. The name of the variable should answer the question "what
is this?", not "what is the type of this?". Often the answer is the
same on the surface (and within a given context), but the distinction
is important.
 
It helps because it makes clear that even though you've selected a  
specific implementation for the variable (i.e. "TextBox"), all that the  
subsequent code cares about is that it's a base type (i.e. "Control").  
Just because the whole body of a method "is one big implementation  
detail", that doesn't mean that the concepts of abstraction and  
simplification aren't useful within it.

Using the code I posted, an example of why this might be useful is if one 
uses TextBoxBase, or even Control, as the variable type, but creates a  
TextBox.  Later, if needed, someone can come along and change from TextBox  
to RichTextBox, without having to worry that there's a use of the variable  
somewhere that depended on the implementation being TextBox.

A similar example would be in dealing with Streams or TextReaders (to name  
a couple of i/o-related classes that have multiple possible implementors  
one might choose from, and which could change over the course of some  
code's lifetime).

That's my point precisely. I do argue that, with methods that are
small enough to be properly readable, there is no such advantage in
practice. For your first example, if there was a use of the specific
member of type TextBox, then we can safely assume that it was either
because it was needed there, or because it was too convenient to use
to ignore it.

To further illustrate the point, I'll consider another commonly
mentioned example - IList<T> instead of List<T>. So what benefits
would I get from using the former instead of the latter _for locals_?
Supposedly that, at some later stage, I can easily plug in a different
implementation. But how often did you actually need that? On the other
hand, consider how much shorter the code can be when using List<T>
convenience methods over generalized IList<T>... it could be said that
it's an atypical example because IList<T> should really offer most if
not all operations available on List<T> in some generic way; but, on
the other hand, if the code only uses operations that are available
through the interface, then it doesn't matter if it uses type
inference or not - you can still switch types. If you can't switch
types because the code uses some member of a derived type, then the
types just aren't switchable, inferred or not; and the member is
likely used for a reason.
In addition to that scenario, there's also the fact that writing the code 
this way can not only help in the event that the code using the variable  
winds up being refactored into a method used by multiple places, it can  
actually _encourage_ doing so.

I'm not sure I follow here. How using or not using type inference
impacts the ability to refactor code? Do you mean that with type
inference it may be harder to spot fragments of code that could be
reused?
Finally, there's the general philosophical belief (which you may or may  
not share) that one should simply stick to the most-constrained usage of  
an object that fits with what's actually needed.  Following that  
philosophy causes the code to be more general, more reusable, and more  
maintainable.  Just because one is working within a method body insteadof  
some other context doesn't change the usefulness of that philosophy.

I think I've already covered that one above. I consider the use of
supertypes over concrete types in non-exposed (i.e. private)
declarations a form of premature pessimization - you're paying for
something that you're very unlikely to benefit from in the future. If
you ever need to change the type, then go ahead and change it, and fix
the code broken by the change - when the dependencies are localized
(which is particularly true for locals), the cost of such fix is far
less than the cost of overgeneralized coding in the first place.
I can't say that nested generics have ever made my eyes bleed.  Seems like  
a non-issue to me.

I think we'll just have to agree to disagree, then. But it would seem
that a lot of people find 40+ chars long type names not particularly
readable - there's a reason why "auto", the C++0x equivalent to "var",
is cheered by the majority of C++ developers, who are tired of having
But in any case, I don't think I used the word "readability" anywhere in  
my comments.  I'm talking about understanding the code, not how easy itis  
to write or read.

You believe that making the code harder or easier to read does not
impact the ease of understanding it?
 
Really?  Even when someone has copied-and-pasted that code into a web  
site?  Or a newsgroup post?

In those _few_ cases when the type matters, and you need to past a bit
of code that's sufficiently removed from its context that the types
are not clear (at which point it's probably not compilable already),
then why not just comment it?

Really, is it something that you have to do often?
 Even when I don't even have Windows running,  
never mind with Visual Studio executing?

I'm not sure what you mean here. If you mean that some other IDEs/
editors can't handle "var" properly when it comes to quick info and
refactoring, then surely it's the problem with those IDEs? I mean,
some people preached some time ago that language keywords should be
ALL UPPERCASE because that way they clearly stand out in the code; the
idea quickly died when syntax highlighting became norm.

If, on the other hand, you mean code _specifically written_ for media
where the convenience of the IDE is not immediately available (the
aforementioned forums/newsgroups, printed books etc), then I can agree
that there it may be advantageous to be more conservative with use of
type inference. Though even there it's much less of a problem in
practice - once again, ML and Haskell programmers don't seem to have
problems with lack of type annotations, why C# should be any
different?
 
Because the code is self-documenting, with no need for a comment, if you  
simply specify the type explicitly.

Well, or that. In any case, this is a minor nuisance at best, and I do
not see "cut&paste-ability into newsgroup post" as a major factor when
evaluating the quality of code.
So far as I can see, the only argument in this particular scenario in  
favor of "var" is that you don't have to type the type name the extra  
time.  But if you type the type name in a comment, you've just typed it 
the extra time.  And then you have to type "//" and "var" as well, not to  
mention the line feed!

Honestly, the typing issue is a non-starter.  I'm being facetious.  But  
however you look at it, it's not an argument in favor of "var" any more  
than it's an argument against it.

Of course not. My argument is not that "var" is better. My argument is
merely that "var" is not any worse, and when it comes to that - why,
yes, when there are two choices that are equally good, of course the
shorter one wins :)
Do code reviews much?  My favorite tool, in a version-controlled setting,  
is still Windiff after all these years.  It doesn't know anything about 
types.  It just shows you what was added, removed, or moved.

I think this is yet another instance of tools lagging behind. There's
no reason why all the benefits provided by a modern IDE (be it VS or
not, and not even in C# context) should suddenly disappear when you
start dealing with code reviewing or version control.

Though in practice, you have an interesting point there. VS doesn't
use its own editor for diffs, so there's no IDE assistance; and I
don't know any third-party tool that would do that. This is bad. On
the other hand, Eclipse does use its Java editor for diffing - and
this is good, and an example to follow. I can concede that avoiding
the use of "var" in C# today may have merit, but only insofar as the
tools are lacking.

Even so... I had dealt with a codebase that started as "use 'var' only
where it's clear" and eventually migrated to "use 'var' wherever it's
more convenient", as we never had a strict rule on that either way. We
never had any problems with understanding the code, in code reviews or
otherwise, in practice. I also do not recall the last time that I
actually had to hover mouse over "var" to see what it expanded to -
because it simply doesn't matter when understanding what the code
does, when it's reasonably clean.
I mean that code is often presented in contexts outside the IDE.  Maybe 
the sarcasm in my reply overloaded your circuits, but my point is that you  
claim that _my_ IDE is capable of showing the type when I hover the mouse 
over the variable name, but for it to be able to do that, it would have to  
accomplish that task in every scenario where I'm looking at code, not just  
when I've loaded it into my IDE.

Well, I've covered that case in my original post.

I still do not see how this applies to actual production code, vast
majority of which is never going to be printed in the book or posted
to a newsgroup. I believe that depriving the developer from handy
convenience features just because there is a remote possibility that
someone, somewhere, might open the code in Vim or Notepad, is a wrong
way to go.
 
The point isn't what to do if there's a use of the specific member of  
TextBox.  It's how to encourage future visitors to the code to not use  
members of that class in the first place, if the original intent was to  
keep the code more general-purpose.

What's the point of keeping the code more general-purpose if it only
deals with local objects, not inputs and outputs?
It's obvious what the benefit of keeping the latter more general is. I
don't see it for the latter, though. What does it buy you?
Which completely invalidates your point.  I've never contended that if you  
_need_ to use the members of the more-derived type, you shouldn't.  By all  
means, use the full type when necessary.

But, if you've got code in a method that really only cares about the  
IList<T>-specific behaviors in the List<T> class, that's easily  
represented with a fully-typed variable declaration as a way of  
documenting that characteristic of the code.

And yet again, I can certainly see the wisdom of documenting that at
method boundary, but inside? Why? We don't write pre- and
postconditions on individual statements within methods, either. If the
method really needs that sort of thing, then arguably it's already
overly long from maintainability perspective.
Likewise, by representing the variable that way, it makes it clear if and 
when someone comes along later and does something that might pollute the  
method with something not quite related, and which really belongs  
elsewhere.

Can you give any specific example of that "pollution" happening?
 Again, it's part of the refactoring process, helping document  
the original intent of the code, so that that can continue to be preserved  
as the code evolves.

The original intent of a piece of code is captured by the contract of
that piece of code - for methods, this is described by method name and
signature and its pre- and post-conditions. Whatever's inside should
not matter at all as long as the contract remains the same. If method
does double duty that needs more explaining, then it's time to split
it into two, and document those separately.
That's part of it, yes.  But the other part is that if the refactoring  
only works when the less-derived type is used, then the refactoring either  
becomes more painful (because types have to be changed, or even sections  
of code that assumed a more-derived type have to be stuck somewhere else),  
or not practical at all (because the more-derived type winds up too  
entrenched, having evolved away from the original intent of the code).

I would argue that use of more-derived type is so heavy that it makes
such a refactoring painful, then the code is clearly distinct enough
that refactoring does not serve a benefit there. I'm sure you yourself
remember some posts from other people in this group who tried to
overgeneralize by refactoring what they thought was common pattern
into a separate method, producing something that ended up being more
complicated in the end.
No.  I believe it does "impact" (I prefer the more traditional "affect",  
but whatever...) the ease of understanding it.  But it's not possible to  
say a priori whether that effect will be positive or negative.  Or, more  
specifically, readability is not the only factor when it comes to  
understanding the code.  You can improve readability, but in a way that 
reduces comprehension.

Good that we can agree on that at least. Now all that's needed is to
quantify the effect of type inference on both readability and
comprehension. I think that, maybe, a few hundred more posts might get
us somewhere on that - most likely "somewhere" being "agree to
disagree". :)

For me, the length and inconclusiveness of this discussion so far is a
strong hint that it should be considered among others such as "tabs or
spaces", "what is the right way to arrange curly braces", "'string' or
'String'", "should 'using' go inside or outside the namespace
declaration" etc - as a matter of personal preference, and a good
subject for a flame war. If it actually had any considerable effect,
there would have been a de-facto standard in place already.
Interesting that you clipped from your quote of my post the metaphor that 
explains exactly this point.  Maybe it just didn't make sense to you?

I did not recognize the specific reference, but I believe that I still
understand what you mean in general.
 
"Form somethingElse" is not good, because the name is not descriptive,
and that's a problem in and of itself. Using or not using "var"
doesn't change it.

It doesnt change it its better of the two options.
It doesn't do that. It says that variable "control" holds a reference
to a control - this does not necessarily imply that there is a type
Control. It does, however, imply that you should be able to do with
"control" any operations that make sense for a control, whatever the
meaning of "control" is in this context.

Yes.
 
It doesnt change it its better of the two options.

Okay, I concede that, in a code that is otherwise rather unreadable,
spelling out types explicitly makes it somewhat less so :)

Alright, here's another interesting point. Note that "var" by far not
the only case where you deal with a type that is not explicitly
specified. Every type you pass a return value of a property or a
method to another method, or chain member access, you're essentially
doing the same thing. Consider:

SetFocus(button.Parent.Controls[0]);

the above is something that can be routinely seen in C# code written
long before "var" was even introduced. It is precisely equivalent in
terms of explicit type information to:

var parent = button.Parent;
var firstChild = parent.Controls[0];
SetFocus(firstChild);

If you are willing to argue that the latter is really better spelled
thus:

Control parent = button.Parent;
Control firstChild = parent.Controls[0];
SetFocus(firstChild);

then, logically, you should also either avoid the original chaining
style entirely, or at least use explicit casts after every operation,
to achieve the same type information load:

SetFocus((Control) ((Control)button.Parent).Controls[0] );

And yes, I know that overly long chains and too much nested calls in a
single expression are frowned upon, and rightly so. However, I believe
that my original example, where those are used in moderation, is
perfectly understandable, and therefore, so is its var-expansion.

Flame on :)
 
Peter,

It is simple make use of a computor like every clerq after his computer does
while writing a letter.

It seems that developers are still in the time of the teletype typewritter,
where they think that letting the computer do things is wrong.

(My F7 with windows mail returns too me that English is no longer available,
however, I wished it was again)

jmo

Cor
 
And contrary to what some people might say it isn't about being too lazy to
type.  Once I have typed "ExtraLongTypeName t = new " VS will suggestthe
class name and I can just hit tab, so it actually takes more keypresses.
The reason I use it is simply because I find I can read the line quicker if
it just says "var t = .........".  Seeing as I am going to read what is to
the right of the = sign anyway I see no point in declaring the type to the
left of it, the "var" merely tells me this is a local variable.

That's certainly a significant benefit... and it's not (IMO) so much
to do with a single type name being long as when you're using a
generic type with perhaps two or three type arguments. Do you really
need to see Dictionary<string, decimal> twice in a line?

There's more to it than that though: Eric Lippert has a very
interesting perspective on this, in terms of whether you want to
emphasis *what* code does or *how* it does it:
http://csharpindepth.com/ViewNote.aspx?NoteID=61

Jon
 
"it doesn't matter a bit whether this thing is a List<Customer> or a
Customer[], what matters is that it is a collection of customers."

Exactly :-)
 
Peter said:
"it doesn't matter a bit whether this thing is a List<Customer> or a
Customer[], what matters is that it is a collection of customers."

Exactly :-)

But couldn't one counter that with "it depends"? Different sorts of
collections have different methods available, so depending on what you
want to do with the collection, it could actually matter?

If you really only want a "collection of customers" why not use some
basic "Collection" type, or define your own "CustomerCollection"?


/Peter
 
Really? Even when someone has copied-and-pasted that code into a web

Why would it matter what the type is? Given the exact code above you still
don't have the definition for the "User" class. You're just making up
requirements on the spot to make a point.

Wow...I had no idea my IDE worked so well. You'll have to tell me how to
enable that feature...it doesn't seem to be activated at the moment.

Remember the conversation (argument) you had recently with a guy about how
you can come across badly? Your post is an example of such an occurence.
 
But couldn't one counter that with "it depends"? Different sorts of
collections have different methods available, so depending on what you
want to do with the collection, it could actually matter?

It doesn't matter what you would like to do with it, you either can do it or
you can't. It depends on the type, not on how you declare it. When you
read my code you will see what I *am* doing, not what I might be able to do.
 
Alright, here's another interesting point. Note that "var" by far not
the only case where you deal with a type that is not explicitly
specified. Every type you pass a return value of a property or a
method to another method, or chain member access, you're essentially
doing the same thing. Consider:

  SetFocus(button.Parent.Controls[0]);

the above is something that can be routinely seen in C# code written
long before "var" was even introduced. It is precisely equivalent in
terms of explicit type information to:

  var parent = button.Parent;
  var firstChild = parent.Controls[0];
  SetFocus(firstChild);

Its a good and valid point. In the example you cited, the code reader
would need to jump to declaration of SetFocus to see the expected
parameter types. The two cases (var and the example above) are
identical in that respect.
If you are willing to argue that the latter is really better spelled
thus:

  Control parent = button.Parent;
  Control firstChild = parent.Controls[0];
  SetFocus(firstChild);

then, logically, you should also either avoid the original chaining
style entirely, or at least use explicit casts after every operation,
to achieve the same type information load:

  SetFocus((Control) ((Control)button.Parent).Controls[0] );

And yes, I know that overly long chains and too much nested calls in a
single expression are frowned upon, and rightly so.
Agreed.

However, I believe
that my original example, where those are used in moderation, is
perfectly understandable, and therefore, so is its var-expansion.

I am still not convinced only because I dont see a reason to do so(use
var) as the alternative(explicit type declaration) is intuitive.
 
Peter said:
"it doesn't matter a bit whether this thing is a List<Customer> or a
Customer[], what matters is that it is a collection of customers."

But couldn't one counter that with "it depends"? Different sorts of
collections have different methods available, so depending on what you
want to do with the collection, it could actually matter?

I would argue that when you see a call like this:

customers.Add(customer);

Then you do not really need to know the type of "customers" _at all_
to understand what's going on here. If, in practice, it is not the
case, then the method "Add" is simply not properly named. Well, or
maybe it's the local variable "customers". Of course you can write
something like:

var customers = 0m;
var customer = 1m;
customers.Add(customer);

and that would be confusing as hell; but it's not because of type
inference!
 
There's more to it than that though: Eric Lippert has a very
interesting perspective on this, in terms of whether you want to
emphasis *what* code does or *how* it does it:http://csharpindepth.com/ViewNote.aspx?NoteID=61

As usual, Eric is very careful with words to get the message across
precisely as it is intended to be... I wish I could write it as
succinctly as that.

It's interesting that this distinction had traditionally manifested
itself along the "what" vs "how" lines in the past, too: functional
languages had type inference - and on a much greater scope! - for
decades now, and it is heavily used, and not considered to be a
nuisance; imperative languages are only now getting there (and it
seems that type inference tends to go along with other features from
FP land, such as lambdas).

Maybe the divide on this matter represents the respective backgrounds
and preferences of people on a wider scale? To someone with C++ and/or
Java background, type inference is a new feature that goes against the
established conventions - as such, it is immediately suspect by
default, and its use has to be definitely justified. To someone with
FP background, it's something that has been missing from mainstream
imperative languages for way too long, and its usefulness has already
been demonstrated elsewhere, so there are no qualms in using it
throughout.

(for the record, my background is mostly C++, but I do consider modern
FP techniques superior for high-level programming)
 
Alright, here's another interesting point. Note that "var" by far not
the only case where you deal with a type that is not explicitly
specified. Every type you pass a return value of a property or a
method to another method, or chain member access, you're essentially
doing the same thing. Consider:
  SetFocus(button.Parent.Controls[0]);
the above is something that can be routinely seen in C# code written
long before "var" was even introduced. It is precisely equivalent in
terms of explicit type information to:
  var parent = button.Parent;
  var firstChild = parent.Controls[0];
  SetFocus(firstChild);

Its a good and valid point. In the example you cited, the code reader
would need to jump to declaration of SetFocus to see the expected
parameter types. The two cases (var and the example above) are
identical in that respect.

The question is - why would you care about the parameter types of
SetFocus? Arguably, if you read someone's code, and see the above - is
there really anything about it that is unclear to you without looking
at the type of SetFocus, or button, or button.Parent?
I am still not convinced only because I dont see a reason to do so(use
var) as the alternative(explicit type declaration) is intuitive.

I don't see how explicit type declaration is "intuitive". If you mean
"traditional", then, yes, it is certainly that, for a language derived
syntactically from C/C++. But one does not imply another.

As for why - obviously, brevity. There's a reason why we don't type
"ADD a TO b GIVING c" anymore :)
 
 The fact is, due to overloads, this sort  
of argument-chaining in methods calls _can_ be less understandable, and  
does suffer exactly the kind of understandability problem I'm talking  
about.

button.Parent.Controls.Count suffers from understandability problem?
What's not understandable about it?
That said, I see it as much less of a problem than a variable declaration,  
because the reference is used only in one place, and no one is ever going 
to come along later and add new uses of that reference.  There's a much 
less urgent need to be explicit about the method calls because of this.  

Localizing dependencies is good, but variable scope can also be
restricted almost arbitrarily by using blocks.
And, why is reusing the reference a problem? Because someone might use
it "inappropriately"? I've yet to see an example of that in this
thread where type of the variable was somehow involved in the
"inappropriateness".
Even though the problems are in fact similar just as you suggest, I don't 
agree that you can conclude the same approach is required in order to  
resolve the issue.

It's obviously not black & white, and I didn't intend to generalize
that case - merely to point out that at least some cases of "var" use
where the initializer type is "not obvious" straight away can
nonetheless be just as readable as understandable as other frequently-
used code patterns.


By the way, one of my original questions still goes unanswered... what
about "let"? No takers? :)
 
Back
Top