Constraints in a Generic Class

Constraining a Parameter to a Structure

When creating a generic class, we saw that you can indicate that it would use a parameter type but you are not specifying the type of that parameter. You can put a restriction to indicate how the compiler should deal with the parameter. You have many options.

To create a constraint on a generic class, after the <type-name> operator, type where TypeName : followed by the rule that the class must follow. The basic formula to create a generic restriction is:

```class class-name<parameter-type>
where T : constraint rule(s)
{

}```

As we will see, there are various types of constraints you can apply to generic classes.

Constraining a Parameter to a Structure Type

When creating a generic class, you can indicate that you want the parameter type to be a structure. To do this, set the constraint rule to struct. Here is an example:

```namespace Exercises.Models
{
class Rectangle<T>
where T : struct
{
}
}```

In this example, the where restriction indicates that a vertex must be a data type that is a structure. Here is an example:

```@page
@model Exercises.Pages.ExerciseModel
@{
NaturalPoint npt1 = new NaturalPoint(0, 2);
NaturalPoint npt2 = new NaturalPoint(2, 0);
NaturalPoint npt3 = new NaturalPoint(0, -2);
NaturalPoint npt4 = new NaturalPoint(-2, 0);

Rectangle<NaturalPoint> rect1 = new Rectangle<NaturalPoint>(npt1, npt2, npt3, npt4);

FloatingPoint fpt1 = new FloatingPoint( 3,  5);
FloatingPoint fpt2 = new FloatingPoint( 3, -5);
FloatingPoint fpt3 = new FloatingPoint(-5, -5);
FloatingPoint fpt4 = new FloatingPoint(-5,  3);

Rectangle<FloatingPoint> rect2 = new Rectangle<FloatingPoint>(fpt1, fpt2, fpt3, fpt4);
}

@functions{
public struct NaturalPoint
{
public int X;
public int Y;

public NaturalPoint(int x = 0, int y = 0)
{
X = x;
Y = y;
}
}

public struct FloatingPoint
{
public double X;
public double Y;

public FloatingPoint(double x = 0d, double y = 0d)
{
X = x;
Y = y;
}
}

public class Rectangle<T>
where T : struct
{
public T vertex1;
public T vertex2;
public T vertex3;
public T vertex4;

public Rectangle(T one, T two, T three, T four)
{
vertex1 = one;
vertex2 = two;
vertex3 = three;
vertex4 = four;
}
}
}```

Constraining a Parameter to a Class Type

To indicate that you want the parameter type of a generic class to be a class type, set the constraint rule to class. Here is an example:

```namespace Exercises.Models
{
class Rectangle<T>
where T : class
{
}
}```

The where restriction in this case indicates that the T parameter must have been created from a class.

Constraining a Parameter to a Specific Class

Imagine you create a regular interface such as the following:

```namespace Exercises.Models
{
public interface IPerson
{
string FullName { get; set; }
DateTime DateofBirth { get; set; }
}
}```

Then imagine you implement it in class. Here is an example:

```namespace Exercises.Models
{
public interface IPerson
{
string FullName { get; set; }
string DateofBirth { get; set; }
}

public class PersonalIdentification : IPerson
{
public PersonalIdentification(string name, string dob)
{
DateofBirth = dob;
FullName    = name;
}

public virtual string FullName    { get; set; }
public virtual string DateofBirth { get; set; }
}
}```

When creating a generic class, you can make it implement the functionality of a certain interface or you can make sure that the class is derived from a specific base class. This would make sure that the generic class contains some useful functionality.

To create a constraint on a generic class, after the <type-name> operator, type where type-name : followed by the rule that the class must follow. For example, you may want the generic class to implement the functionality of a pre-defined class. You can create the generic class as follows:

```namespace Exercises.Models
{
public interface IPerson
{
string FullName { get; set; }
string DateofBirth { get; set; }
void Display();
}

public class Employee<T>
where T : PersonalIdentification
{
}
}```

After creating the class, you must implement the virtual members of the where class/interface, using the rules of generic classes, the way we have done it so far.

```namespace Exercises.Models
{
public interface IPerson
{
string FullName { get; set; }
string DateofBirth { get; set; }
}

public class PersonalIdentification : IPerson
{
public PersonalIdentification(string name, string dob)
{
DateofBirth = dob;
FullName    = name;
}

public virtual string FullName    { get; set; }
public virtual string DateofBirth { get; set; }
}

public class Employee<T>
where T : PersonalIdentification
{
public Employee()
{
}

public Employee(T record)
{
Identification = record;
}

public T Identification { get; set; }
}
}```

When declaring a variable for the generic class, in its <> operator, you must enter an object of the base class. Here is an example:

```@page
@model Exercises.Pages.ExerciseModel
@{
var std = new PersonalIdentification("James Sandt", "12/08/2024");

Employee<PersonalIdentification> empl = new Employee<PersonalIdentification>();

empl.Identification = std;
}

@functions{
public interface IPerson
{
string FullName { get; set; }
string DateofBirth { get; set; }
}

public class PersonalIdentification : IPerson
{
public PersonalIdentification(string name, string dob)
{
DateofBirth = dob;
FullName = name;
}

public virtual string FullName { get; set; }
public virtual string DateofBirth { get; set; }
}

public class Employee<T>
where T : PersonalIdentification
{
public Employee()
{
}

public Employee(T record)
{
Identification = record;
}

public T Identification { get; set; }
}
}

<pre>Personal Identification
---------------------------------
Full Name:     @empl.Identification.FullName
Date Of birth: @empl.Identification.DateofBirth
=================================</pre>```

This would produce:

```Personal Identification
---------------------------------
Full Name: James Sandt
Date Of birth: 12/08/2024
=================================```

Consider the following Physician class:

```namespace Exercises.Models
{
public interface IPerson
{
string FullName { get; set; }
string DateofBirth { get; set; }
}

public interface IProfession
{
string Category { get; set; }
}

public class Physician
{
public string Category { get; set; }
}

public class PersonalIdentification : IPerson
{
public PersonalIdentification(string name, string dob)
{
DateofBirth = dob;
FullName    = name;
}

public virtual string FullName    { get; set; }
public virtual string DateofBirth { get; set; }
}

public class Employee<T>
where T : PersonalIdentification
{
public Employee()
{
}

public Employee(T record)
{
Identification = record;
}

public T Identification { get; set; }
}
}```

Based on the restrictions, you cannot use just any class as the parameter of the generic. For example, the following would produce an error:

```@page
@model Exercises.Pages.ExerciseModel
@{
var std = new PersonalIdentification("James Sandt", "12/08/2024");

Employee<PersonalIdentification> empl = new Employee<PersonalIdentification>();

empl.Identification = std;
}

@functions{
public interface IPerson
{
string FullName { get; set; }
string DateofBirth { get; set; }
}

public interface IProfession
{
string Category { get; set; }
}

public class Physician
{
public string Category { get; set; }
}

public class PersonalIdentification : IPerson
{
public PersonalIdentification(string name, string dob)
{
DateofBirth = dob;
FullName = name;
}

public virtual string FullName { get; set; }
public virtual string DateofBirth { get; set; }
}

public class Employee<T>
where T : PersonalIdentification
{
public Employee()
{
}

public Employee(T record)
{
Identification = record;
}

public T Identification { get; set; }
}
}

<pre>Personal Identification
---------------------------------
Full Name:     @empl.Identification.FullName
Date Of birth: @empl.Identification.DateofBirth
=================================</pre>

@{
var doctor = new Physician();

doctor.Category = "Health Care";
Employee<Physician> rn = new Employee<Physician>();
}```

This would produce:

```Severity	Code	Description	Project	File	Line	Suppression State
Error (active)	CS0311	The type 'Exercises.Pages.Pages_Exercise.Physician' cannot be used as type parameter 'T' in the generic type or method 'Pages_Exercise.Employee<T>'. There is no implicit reference conversion from 'Exercises.Pages.Pages_Exercise.Physician' to 'Exercises.Pages.Pages_Exercise.PersonalIdentification'.	Exercises	F:\Users\mything\source\repos\RazorPages\Exercises\Exercises\Pages\Exercise.cshtml	66```

You can also create a constraint so that a generic class implements an interface.

A new Default Restriction

Depending on the behavior you want a class to have, you may want to require that a generic class that uses a parameter must also have a default constructor. To put this restriction, you use the new keyword as a constraint. The primary formula to follow is:

`class ClassName<T> where T : new()`

The new factor in this formula is the new keyword. Here is an example of using it:

```namespace Exercises.Models
{
public interface IPerson
{
string FullName    { get; set; }
string DateofBirth { get; set; }
}

public class PersonalIdentification : IPerson
{
public PersonalIdentification(string name, string dob)
{
DateofBirth = dob;
FullName    = name;
}

public virtual string FullName    { get; set; }
public virtual string DateofBirth { get; set; }
}

public class Employee<T>
where T : new()
{
public Employee()
{
}

public Employee(T record)
{
Identification = record;
}

public T Identification { get; set; }
}
}```

This new operator indicates that the class (or structure) that the T parameter represents must have a default constructor. Remember that when this code executes, the Employee<> class doesn't know what T means or represents. This means that the above code will compile just fine. It is when you declare a variable of the Employee<> type that the compiler is informed about the T parameter. That's when it checks the class that T represents. If that class doesn't have a default constructor, you would receive an error. Here is an example:

```@page
@model Exercises.Pages.ExerciseModel
@{
var std = new PersonalIdentification("James Sandt", "12/08,2025");

Employee<PersonalIdentification> empl = new Employee<PersonalIdentification>();

empl.Identification = std;
}

@functions{
public interface IPerson
{
string FullName { get; set; }
string DateofBirth { get; set; }
}

public class PersonalIdentification : IPerson
{
public PersonalIdentification(string name, string dob)
{
DateofBirth = dob;
FullName = name;
}

public virtual string FullName    { get; set; }
public virtual string DateofBirth { get; set; }
}

public class Employee<T>
where T : new()
{
public Employee()
{
}

public Employee(T record)
{
Identification = record;
}

public T Identification { get; set; }
}
}```

This would produce:

```Severity	Code	Description	Project	File	Line	Suppression State
Error (active)	CS0310	'Pages_Exercise.PersonalIdentification' 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 'Pages_Exercise.Employee<T>'	Exercises	F:\Users\mything\source\repos\RazorPages\Exercises\Exercises\Pages\Exercise.cshtml	6```

The correction is to make sure that the class that T represents has a default constructor. Here is an example:

```using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Exercises.Pages
{
public interface IPerson
{
string FullName    { get; set; }
string DateofBirth { get; set; }
}

public class PersonalIdentification : IPerson
{
public PersonalIdentification()
{
}

public PersonalIdentification(string name, string dob)
{
DateofBirth = dob;
FullName    = name;
}

public virtual string FullName    { get; set; }
public virtual string DateofBirth { get; set; }
}

public class Employee<T>
where T : new()
{
public Employee()
{
}

public Employee(T record)
{
Identification = record;
}

public T Identification { get; set; }
}
}```

Constraining Various Parameters

Remember that a generic class can use more than one parameter. Here is an example:

```using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Exercises.Pages
{
public class Employee<T, P>
{
}
}```

If you want to set a restriction of the parameters, use a where operator for each. Here is an example:

```using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Exercises.Pages
{
public interface IPerson
{
string FullName    { get; set; }
string DateofBirth { get; set; }
}

public interface IProfession
{
string Category { get; set; }
}

public class Profession
{
public string Category { get; set; }
}

public class PersonalIdentification : IPerson
{
public PersonalIdentification(string name, string dob)
{
DateofBirth = dob;
FullName = name;
}

public virtual string FullName { get; set; }
public virtual string DateofBirth { get; set; }
}

public class Employee<T, P>
where T : PersonalIdentification
where P : Profession
{
public Employee()
{
}

public Employee(T record, P earn)
{
Occupation     = earn;
Identification = record;
}

public T Identification { get; set; }
public P Occupation     { get; set; }
}
}```

When declaring a variable for the class, provide the appropriate type. Here is an example:

```@page
@model Exercises.Pages.ExerciseModel
@{
var persID = new PersonalIdentification("Jeannot Schwartz", "12/08,2025");

var pro = new Profession();

pro.Category = "Health Care";

Employee<PersonalIdentification, Profession> surgeon = new Employee<PersonalIdentification, Profession>();

surgeon.Identification = persID;
surgeon.Occupation = pro;
}

@functions{
public interface IPerson
{
string FullName    { get; set; }
string DateofBirth { get; set; }
}

public interface IProfession
{
string Category { get; set; }
}

public class Profession
{
public string Category { get; set; }
}

public class PersonalIdentification : IPerson
{
public PersonalIdentification(string name, string dob)
{
DateofBirth = dob;
FullName = name;
}

public virtual string FullName    { get; set; }
public virtual string DateofBirth { get; set; }
}

public class Employee<T, P>
where T : PersonalIdentification
where P : Profession
{
public Employee() { }

public Employee(T record, P earn)
{
Occupation = earn;
Identification = record;
}

public T Identification { get; set; }
public P Occupation     { get; set; }
}
}

<pre>Personal Identification
---------------------------------
Category: @surgeon.Occupation.Category
=================================</pre>```

This would produce:

```Personal Identification
---------------------------------
Category: Health Care
=================================```

Remember that any regular class can implement an interface. In the same way, a normal generic class can implement any interface of your choice. Here is an example:

```namespace Exercises.Models
{
public class Employee<T>
where T : IPerson
{
}
}```

On such a class, you can also put a restriction that the class that T represents must have a default constructor. In this case, the new operator must be set as the last. Here is an example:

```@functions{
public class Employee<T>
where T : IPerson, new()
}```