Stretching Type Inference
Note: Code in italics is not actual C# 3.0 syntax.
Local Variable Type Inference
C# 3.0 brought us local variable type inference mainly because of LINQ. The output of a query can vary from an IEnumerable<T> or an IQueryable<T> to a single instance of T where T can even be a projection which means that its type is an anonymous type.
Take the following query as an example:
from p in persons select new { Name = p.FirstName + " " + p.LastName, Age = p.Age };
If persons is an IEnumerable<Person>, the output of the query will be an IEnumerable<T> where T is an anonymous type.
Now the question that arises is: How do I declare a variable to receive the output of the query?
The solution was the introduction of the new var keyword that instructs the compiler to determine the type of the output of the query. Now, all it takes to declare a variable to receive the output of the query is:
var q = from p in persons select new { Name = p.FirstName + " " + p.LastName, Age = p.Age };
Since the compiler can infer the type of something that looks so complex, it's obvious that it can infer the type of something so straightforward as:
var v = new Dictionary<Point, Stack<Person>>();
It's obvious that this is the same as:
Dictionary<Point, Stack<Person>> v = new Dictionary<Point, Stack<Person>>();
with less typing and margin for typing errors.
Field Type Inference
The same technique could be applied to field inference:
class Class { private someDicionary = new Dictionary<string, List<string>>();<SPAN style="COLOR: green">// ...
}
with the same benefits of less typing and margin for typing errors.
If field type inference were to be applied to public fields, one constraint must be enforced though: the inferenced type cannot be or have as a type parameter an anonymous type.
But I wouldn't recommend that because it would be easy and not obvious that a small change in the field initialization could bring unobvious changes to the public interface of the class.
Method Output And Parameter Type Inference
Since type inference would be possible outside of methods, should it be allowed on method output and parameter values? Something like this:
class SomeClass { private listOfStuff = new List<Stuff>();<SPAN style="COLOR: blue">public void </SPAN>DoIt() { <SPAN style="COLOR: blue">if </SPAN>(CreateList(<SPAN style="COLOR: blue">out this</SPAN>.listOfStuff)) { ProcessList(<SPAN style="COLOR: blue">ref this</SPAN>.listOfStuff); <SPAN style="COLOR: blue">this</SPAN>.listOfStuff = TransformList(<SPAN style="COLOR: blue">this</SPAN>.listOfStuff); } }
private bool CreateList(var out list) { list = new List<Stuff>(); return true; }
private void ProcessList(var ref list) { // ... }
private var TransformList(var list) { return list; } }
which look very strange and confusing, to say the least. We shouldn't go there.
Constructor Inference
Every since I saw local variable type inference the first time, I have the feeling that more could have been done.
Let's look at this hypothetical set of declarations:
Dictionary<Point, Stack<Person>> v = new(); Point p = new(1, 2);
It's as easy to the compiler or a human reading the code that this is the same as:
Dictionary<Point, Stack<Person>> v = new Dictionary<Point, Stack<Person>>(); Point p = new Point(1, 2);
And this can be more powerful than local variable type inference and used in a lot more scenarios:
class Class { private Dictionary<string, List<string>> someDicionary = new(); private Point p;<SPAN style="COLOR: blue">public </SPAN>Class() {
this.p = new(1, 1); }
<SPAN style="COLOR: blue">public void </SPAN>SomeMethod() {
AnotherMethod(new()); }
<SPAN style="COLOR: blue">private void </SPAN>AnotherMethod(<SPAN style="COLOR: #2b91af">List</SPAN><<SPAN style="COLOR: blue">string</SPAN>> arg) { <SPAN style="COLOR: green">// ... </SPAN>}
}
Where To Stop?
The compiler could as easily infer the type of a local variable in this:
public void SomeMethod() { var v = new(); AnotherMethod(v); }
Do we want to go there? I don't think so.
Ambiguities
C# 3.0 also brought object initializers. Instead of this:
XmlReaderSettings settings = new XmlReaderSettings(); settings.CheckCharacters = false; settings.IgnoreWhitespace = true;XmlReader reader = XmlReader.Create("someURI", settings);
we can just write this:
XmlReaderSettings settings = new XmlReaderSettings() { CheckCharacters = false, IgnoreWhitespace = true };XmlReader reader = XmlReader.Create("someURI", settings);
XmlReader reader = XmlReader.Create("someURI", new XmlReaderSettings() { CheckCharacters = false, IgnoreWhitespace = true });
Wouldn't it be nice to just write:
XmlReaderSettings settings = new() { CheckCharacters = false, IgnoreWhitespace = true };
C# 3.0 object initializers also allow constructor parenthesis to be omitted:
XmlReaderSettings settings = new XmlReaderSettings { CheckCharacters = false, IgnoreWhitespace = true };
which would lead to:
XmlReaderSettings settings = new { CheckCharacters = false, IgnoreWhitespace = true };
which looks exactly like an anonymous object creation expression and could lead to some confusion and ambiguities.
But it would be nice to just write:
XmlReader reader = XmlReader.Create("someURI", new { settings.CheckCharacters = false, settings.IgnoreWhitespace = true });
Wouldn't it?