views:

110

answers:

1

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
    }
}
+5  A: 

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...

Reed Copsey
@Joshua: The C# compiler generates the code I pasted above. This is how it works - it's not making a parameterless constructor, but rather an optional one. I'll edit to add details...
Reed Copsey
Ok, thanks, that explains why it doesn't work. I still wonder what the implications of generating the parameterless constructor would be.
Joshua Flanagan
@Joshua: I edited my answer to explain why this is potentially a bad idea... Make more sense now? Remember, it's not just optional, you can also use named parameters, so N parameters means N! possible combinations that would need to be generated...
Reed Copsey
... and now you've described them. Well done.
Joshua Flanagan