C# Generics – Why new() Constraint Is Not Satisfied by Class with Optional Parameters

c#-4.0genericsoptional-parameters

The following code fails to compile, producing a "Widget must be a non-abstract type with a public parameterless constructor" error. I would think that the compiler has all of the information it needs. Is this a bug? An oversight? Or is there some scenario where this would not be valid?

public class Factory<T> where T : new()
{
    public T Build()
    {
        return new T();
    }
}

public class Widget
{
    public Widget(string name = "foo")
    {
        Name = name;
    }

    public string Name { get; set; }
}

public class Program
{
    public static void Main()
    {
        var widget = new Widget(); // this is valid
        var factory = new Factory<Widget>(); // compiler error
    }
}

Best Answer

While this logically should work, it unfortunately does not. The CLR still sees your constructor as a parameter-based constructor.

Remember that, while C# supports optional parameters, this is done at the compiler level, at compile time. The underlying type still only contains a constructor taking a single parameter . As far as the CLR is concerned, the "default parameters" are converted to attributes, like so:

public Widget(([Optional, DefaultParameterValue("foo")] string name) { // ...

The CLR is a multi-language runtime. Generics are made to work at the CLR level, for all languages, so the constraints must be true in languages without default parameters, as well. Languages are not required to understand the OptionalAttribute, nor the DefaultParameterValueAttribute, so this cannot work uniformly for all languages, hence it's not allowed.


Edit:

In response to your comment:

What I don't understand is why the C# compiler cannot generate the necessary code to satisfy the CLR

Theoretically, the C# compiler team could have the language generate two separate constructors, instead of one constructor marked with attributes. This would, potentially, explode into many constructors, as named parameters create the capabilities for many, many possible combinations of "constructors" (or method calls for methods), especially when multiple arguments are available. I personally am glad that they did not, since it would cause confusion due to an overabundance of methods and constructors in the generated types, which would cause the public API to look very different than the code that generated it. Take the following constructor:

public Widget(
          int id = 0, 
          string name = "foo", 
          float width=1.0f, 
          float height=1.0f, 
          float depth=1.0f
       ) { // ... 

Were you to automatically generate all of the possible combinations here, the compiler would need to generate 120 constructors for this single constructor, since there are N! possible ways to call this...

Related Question