Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor with optional parameter violates new() constraint

I have a class with this constructor:

public Currency(Guid? vcurrencyUI = null)
    : base(vcurrencyUI)
{ }

and I want to use this class with a new() constraint but I get this error:

'Currency' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method ...

If I split the constructor everything works fine:

public Currency(Guid? vcurrencyUI)
    : base(vcurrencyUI)
{ }

public Currency()
    : base()
{ }

why do I need to split the constructor?

like image 963
Jan S Avatar asked Apr 30 '14 14:04

Jan S


2 Answers

Because a constructor with a default parameter is not a parameterless constructor.

Default parameters are "filled in" by the compiler at compile time. When you write:

var foo = new Currency();

The compiler generates:

var foo = new Currency(null);

When the class is compiled, the compiler creates a constructor that takes that Guid? parameter, and also generates some metadata that says in effect "if the parameter isn't supplied at compile time, then supply null." But no parameterless constructor is generated for the type.

The new() constraint requires that a parameterless constructor be defined for the type, and it won't accept a constructor with a single default parameter. Most likely that's because the runtime, which ends up having to call the constructor, doesn't understand the concept of default parameters.

like image 60
Jim Mischel Avatar answered Oct 02 '22 14:10

Jim Mischel


Although Jim already answered your question, note that a more general approach might be to allow passing a delegate which would instantiate your concrete class, instead of forcing all your implementations to be parameterless.

I.e. instead of this:

public class Something<T> where T : new()
{
    public T CreateInstance()
    {
        return new T();
    }
}

You can pass an explicit delegate which will do any custom instantiation logic:

// note that the constraint is now removed
public class Something<T>
{
    private readonly Func<T> _ctor;
    public Something(Func<T> ctor)
    {
        _ctor = ctor;
    }

    public T CreateInstance()
    {
        return _ctor();
    }
}

// and you can now pass arbitrary constructor logic as a delegate
var x = new Something<Currency>( () => new Currency(null) );

This also allows you to create a helper class and have both options readily available:

public class Something
{
    // this allows you to use a parameterless ctor
    public static Something<T> Create<T>() where T : new()
    {
        return new Something<T>(() => new T());
    }

    // this allows you to specify a custom one
    public static Something<T> Create<T>(Func<T> ctor)
    {
        return new Something<T>(ctor);
    }
}
like image 22
Groo Avatar answered Oct 02 '22 14:10

Groo