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
}
}
where T : new() Means that the type T must have a parameter-less constructor. Having this constraint will allow you to do something like T field = new T(); in your code which you wouldn't be able to do otherwise. You then combine the two using a comma to get: where T : class, new()
Value type constraint If we declare the generic class using the following code then we will get a compile-time error if we try to substitute a reference type for the type parameter.
A generic type is declared by specifying a type parameter in an angle brackets after a type name, e.g. TypeName<T> where T is a type parameter.
Object, you'll apply constraints to the type parameter. For example, the base class constraint tells the compiler that only objects of this type or derived from this type will be used as type arguments. Once the compiler has this guarantee, it can allow methods of that type to be called in the generic class.
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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With