简体   繁体   中英

Constraints on type parameters - new()

As per MSDN , the new() constraint is used to ensure that the type argument must have a public parameterless constructor . However, consider the example given below (taken from the same page).

public class Employee
{
    public Employee(string name, int id)
    {
        Name = name;
        ID = id;
    }
    public string Name { get; set; }
    public int ID { get; set; }
}

class EmployeeList<T> where T : Employee, new()
{

}

Here, the Employee type does not have a parameterless/default constructor, yet this code compiles successfully. Can someone please elaborate on the usage of this constraint and why this works?

The code compiles because it's entirely possible to have a type derived from Employee with a parameterless constructor - and that's what the constraint says. The constraint is not trying to say that Employee itself has a parameterless constructor, and indeed an attempt to use EmployeeList<Employee> would fail as the constraint isn't satisfied.

As example of what would be valid:

public class GeneratedEmployee : Employee
{
    public GeneratedEmployee() : base(GenerateName(), GenerateId())
    {
    }

    private static string GenerateName()
    {
        // Implementation here
    }

    private static int GenerateId()
    {
        // Implementation here
    }
}

At that point, it's fine to create an EmployeeList<GeneratedEmployee> , and assuming the EmployeeList<> class uses new() somewhere in the implementation, it would call the parameterless constructor of GeneratedEmployee .

That said, it's a pretty odd constraint, as I wouldn't expect you to really want to create an employee without specifying the name and ID.

class EmployeeList<T> where T : Employee, new()
{

}

T must be Employee descendant of Employee with parameterless constructor but the fact that Employee does not have one does not mean that it's descendants can't have it.

ie new() constraint will prevent new EmployeeList<Employee>(); from compiling, but you can do:

class EmployeeChild : Employee
{
    public EmployeeChild() : base("", 1)
    {
    }
}

Which will satisfy all constraints and new EmployeeList<EmployeeChild>(); will compile just fine.

Your list says that the class must be an Employee, or derived from Employee. Employee itself does not have a parameterless constructor, so it would fail as the type argument:

//fails to compile
var xx = new EmployeeList<Employee>()

But, if you defined a class that inherited from Employee:

public class EmployeeDerived : Employee
{
    public EmployeeDerived() : base("test", 123)
    {

    }
}

This class does have a parameterless constructor (though it's pretty useless). So the compiler can use it as the T to EmployeeList

Even though Employee doesn't have a parameterless constructor, a subclass of Employee can !

public class Manager: Employee {
    // these parameters don't make sense - just an example
    public EmployeeSubclass(): base("foo", 1) {
        
    }
}

Then I can use Manager as the type argument for EmployeeList - EmployeeList<Manager> . It satisfies both constraints!

Here, the Employee type does not have a parameterless/default constructor, yet this code compiles successfully

That's correct, however what you haven't tried is to actually use the class. What doesn't compile is actually trying to call EmployeeList<Employee> :

CS0310 'Employee' 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 'EmployeeList<T>'

As you correctly quote, Employee doesn't match the constraint T: Employee, new() . The only way to give it something that will match it is to derive a class from Employee that does implement a default constructor, and provide that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM