When learning C# (and OO concepts more generally at the same time) something I found very distracting is exemplified by the following line:
ExampleClass exampleObject = new ExampleClass();
It’s the apparent redundant repetition that bothered me. Some light dawned when I grasped the difference between declaring a variable and assigning it a value or reference (and that these processes can be separate). However, the above usage is extremely common.
Obviously, I’ve learnt to live with it (and am not expecting the C# syntax to change!) but I wondered if there is a better way. Would there be any merit in the following:
exampleObject = new ExampleClass();
Where the variable exampleObject
is strongly typed as ExampleClass
when it is not specified?
EDIT
[after commenters pointed out ambiguity of above between a declaration of a new, and an assignment, of an existing, variable:]
An alternative hypothetical shortened syntax, which avoids the declaration/assignment ambiguity, is:
ExampleClass exampleObject = new();
…where this would create a new instance of ExampleClass
, the type having been specified in the declaration of its referencing variable, exampleObject
.
.. but I’m sure this opens up other problems I haven’t thought of!
/EDIT
Obviously, if for some reason we wanted exampleObject
to be of type Object
, or – a more common requirement – to be of the type of a parent class of ExampleClass
, we would specify these types in front of the variable name in the standard way, but is there any drawback to my suggested shorthand in this very common scenario?
3
C# requires you to explicitly declare a variable, in addition to assigning it a value. This is done so that its clearer to both the compiler and the person reading the code that we’re defining a new variable here, not using a previously-defined one, which can simplify parsing and make the code much clearer.
As you said, the statement ExampleClass c = new ExampleClass()
is simply a contraction of both these actions, which is why you feel the duplication – it’s two different actions. But it does feel clunky. This is why the C# 3.0 specifications (in VS2008) added the var
keyword, which lets you skip a lot of the verbosity, even if it wasn’t the primary motivation for the feature.
Using var
, our example code becomes var c = new ExampleClass()
– much easier to scan, but still explicitly declaring as well as assigning. It gets even more useful with long class names – var complexDictionary = new Dictionary<Tuple<int,int>, List<string>>()
, for instance. Imagine writing that twice!
Of course, var
was originally introduced to support anonymous types. These are used by lambda expressions and LINQ, and create types with names that are both clumsy (MyMethod<>Class5
, or similar) and also not known to the developer, so that saves us a lot of problems.
Additionally, in your edit, you proposed a similar but inverse solution to the duplication – to keep the declaration explicit, but the instantiation implicit. This saves about as many characters as var
, but is a lot less flexible. Consider that variable declaration is very simple – you simply state the type of your variable. Initialization, though, is more complex. Sometimes you simply use new
, and then we can use your new()
syntax. But often we have different constructor parameters. Or how about initializing using a factory method, like ExampleClass exampleObject = ExampleBuilder.CreateExample();
? Your syntax doesn’t save us anything there, whereas the type inference in var
will allow us to use var exampleObject = ExampleBuilder.CreateExample();
smoothly.
3
No, the repetitious syntax shouldn’t bother anyone. It’s kind of like complaining about the the three statements within a for
loop.
for(int x = 0; x <= 100; x++) {
/* do work */
}
Sure, you could use syntactic sugar for mocking this common pattern as below, but you don’t gain a lot by any sensible alternative and, like any usage of sugar, you lose some things that are important.
for(x = 0 to 100) {
/* do work */
}
While this is useful for the simple iteration, it’s shortcomings become clear after a bit of thought:
- It’s not possible in the shorthand for
x
to be anything but a locally declared variable. - You can’t have a more complex test, such as “skip prime numbers”
- You can’t implicitly step by more than 1, or increment some external modifier.
Most of these can be overcome, but the syntax doesn’t really help beyond some typing.
It’s probably worth nothing that your one-line examples actually distort how things really should be done in C#. If we’re initializing a member of a class, you really do want to do something like the following:
class Test : ITest {
public IExample example;
public Test() {
example = new Example();
}
}
- The type of your member is declared by way of an interface, so you can change the concrete class latter without a major code re-write.
- The actual initialization is in a constructor, which can be adjusted or over-ridden in a deriving class.
- Words like “Object” and “Class” are skipped entirely. Names are for people, and the java-like capitalization syntax does enough for our purposes.
Conversely, if you’re only initalizing a local instance, you would do one of the following:
IExample example = new Example();
or
var example = new Example();
5
Yes, it bothers other people. It’s a major motivation for people choosing dynamically typed languages. What you’re talking about in the context of a statically typed language is called type inference, which basically means the compiler can tell what type a variable is by what type it is initialized to. It has been a full feature in languages like Scala and Haskell from the start, because of the crazy long types that come up regularly in those languages. C# supports it in a more limited form with var
, and C++11 with auto
.
However, note that in languages with type inference, it’s still recommended to explicitly specify types on public interfaces to your code. It prevents you from accidentally changing the public interface, say if something is implemented in terms of a concrete type, but you want its public interface to be a more abstract type. It also makes it easier for consumers of your code to determine how to use it.
2