Topics on Generics
Topics on Generics
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:
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:
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;
}
}
public class Exercise
{
private void Create()
{
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);
}
}
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:
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:
public interface IPerson
{
string FullName { get; set; }
DateTime DateofBirth { get; set; }
}
Then imagine you implement it in class. Here is an example:
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:
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.
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:
using static System.Console;
Title = "Personal Identification";
WriteLine("Personal Identification");
WriteLine("---------------------------------");
var std = new PersonalIdentification("James Sandt", "12/08/2002");
Employee<PersonalIdentification> empl = new Employee<PersonalIdentification>();
empl.Identification = std;
WriteLine("Full Name: {0}", empl.Identification.FullName);
WriteLine($"Date Of birth: {empl.Identification.DateofBirth}");
WriteLine("=================================");
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; }
}
This would produce:
Personal Identification --------------------------------- Full Name: James Sandt Date Of birth: 12/08/2002 ================================= Press any key to close this window . . .
Consider the following Physician class:
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:
using static System.Console;
Title = "Personal Identification";
WriteLine("Personal Identification");
WriteLine("---------------------------------");
var std = new PersonalIdentification("James Sandt", "12/08/2002");
Employee<PersonalIdentification> empl = new Employee<PersonalIdentification>();
empl.Identification = std;
WriteLine(string.Format("Full Name: {0}", empl.Identification.FullName));
WriteLine(string.Format("Date Of birth: {0}", empl.Identification.DateofBirth));
var doctor = new Physician();
doctor.Category = "Health Care";
Employee<Physician> rn = new Employee<Physician>();
WriteLine("=================================");
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; }
}
This would produce:
Error 1 The type 'Physician' cannot be used as type parameter 'T' in the generic type or method 'Employee<T>'. There is no implicit reference conversion from 'Physician' to 'PersonalIdentification'. C:\Temporary Projects\WhereGeneric\Exercise.cs 115 18 WhereGeneric
You can also create a constraint so that a generic class implements an interface.
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:
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:
using static System.Console;
Title = "Personal Identification";
WriteLine("Personal Identification");
WriteLine("---------------------------------");
var std = new PersonalIdentification("James Sandt", "12/08,2002");
Employee<PersonalIdentification> empl =
new Employee<PersonalIdentification>();
empl.Identification = std;
WriteLine("=================================");
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:
Error 1 '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 'Employee<T>' C:\. . .\Temporary Projects\WhereGeneric\Exercise.cs 71 18 WhereGeneric
The correction is to make sure that the class that T represents has a default constructor. Here is an example:
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:
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:
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:
using static System.Console;
Title = "Personal Identification";
WriteLine("Personal Identification");
WriteLine("---------------------------------");
var persID = new PersonalIdentification("Jeannot Schwartz", "12/08,2002");
var pro = new Profession();
pro.Category = "Health Care";
Employee<PersonalIdentification, Profession> surgeon =
new Employee<PersonalIdentification, Profession>();
surgeon.Identification = persID;
surgeon.Occupation = pro;
WriteLine("Category: {0}", surgeon.Occupation.Category);
WriteLine("=================================");
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; }
}
This would produce:
Personal Identification --------------------------------- Category: Health Care ================================= Press any key to close this window . . .
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:
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:
public class Employee<T>
where T : IPerson, new()
|
|
|||
| Previous | Copyright © 2012-2026, FunctionX | Tuesday 19 October 2021 | Next |
|
|
|||