hits counter

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>>();

    // ...
}

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>();

    public void DoIt()
    {
        if (CreateList(out this.listOfStuff))
        {
            ProcessList(ref this.listOfStuff);

            this.listOfStuff = TransformList(this.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;

    public Class()
    {
        this.p = new(1, 1);
    }

    public void SomeMethod()
    {
        AnotherMethod(new());
    }

    private void AnotherMethod(List<string> arg)
    {
        // ...
    }
}
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);

or this:

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?

No Comments