Topics on Generics Classes
Topics on Generics Classes
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.
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()
}
|
|||
Previous | Copyright © 2008-2022, FunctionX | Saturday 05 March 2022 | Home |
|