Generic Classes and Inheritance

Introduction

Consider the following geometric figures:

Square Rectangle Trapezoid Parallelogram
Square Rectangle Trapezoid Parallelogram

Notice that these are geometric figures with each having four sides. From what we know so far, we can create a base class to prepare it for inheritance. If the class is very general, we can make it a generic one. We can set a data type as an unknown type, anticipating that the dimensions of the figure can be considered as integer or double-precision types. Here is an example:

public class Quadrilateral<T>
{
    protected T _base;
    protected T _height;
    protected string? _name;

    public virtual T Base
    {
        get { return _base; }
        set { _base = value; }
    }

    public virtual T Height
    {
        get { return _height; }
        set { _height = value; }
    }

    public virtual string? Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public Quadrilateral(string name = "Quadrilateral")
    {
        _name = name;
    }

    public Quadrilateral(T bs, T height)
    {
        _name = "Quadrilateral";
        _base = bs;
        _height = height;
    }

    public Quadrilateral(string name, T bs, T height)
    {
        _name = name;
        _base = bs;
        _height = height;
    }

    public virtual string Describe()
    {
        return "A quadrilateral is a geometric figure with four sides";
    }

    public virtual string ShowCharacteristics()
    {
        return $"Geometric Figure: {Name}, Description: {Describe()}, Base: {Base}, Height: {Height}";
    }
}

You can then use the class by declaring a variable of it. Here are examples:

using static System.Console;

Title = "Geometry: Quadrilaterals";
WriteLine("Geometry: Quadrilaterals");
WriteLine("--------------------------------");

// Trapezoid with equal sides
var kite = new Quadrilateral<double>("Beach Kite", 18.64, 18.64);

WriteLine(kite.ShowCharacteristics());
            
// Rectangle, in meters
var basketballStadium = new Quadrilateral<Byte>();

basketballStadium.Name = "Basketball Stadium";
basketballStadium.Base = 15;
basketballStadium.Height = 28;

WriteLine(basketballStadium.ShowCharacteristics());
WriteLine("=================================");

public class Quadrilateral<T>
{
    protected T _base;
    protected T _height;
    protected string? _name;

    public virtual T Base
    {
        get { return _base; }
        set { _base = value; }
    }

    public virtual T Height
    {
        get { return _height; }
        set { _height = value; }
    }

    public virtual string? Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public Quadrilateral(string name = "Quadrilateral")
    {
        _name = name;
    }

    public Quadrilateral(T bs, T height)
    {
        _name = "Quadrilateral";
        _base = bs;
        _height = height;
    }

    public Quadrilateral(string name, T bs, T height)
    {
        _name = name;
        _base = bs;
        _height = height;
    }

    public virtual string Describe()
    {
        return "A quadrilateral is a geometric figure with four sides";
    }

    public virtual string ShowCharacteristics()
    {
        return $"Geometric Figure: {Name}, Description: {Describe()}, Base: {Base}, Height: {Height}";
    }
}

This would produce:

Geometry: Quadrilaterals
--------------------------------
Geometric Figure: Beach Kite, Description: A quadrilateral is a geometric figure
 with four sides, Base: 18.64, Height: 18.64
Geometric Figure: Basketball Stadium, Description: A quadrilateral is a geometri
c figure with four sides, Base: 15, Height: 28
=================================
Press any key to close this window . . .

The Nullity of a Generic Parameter

We have seen that, when you are creating a generic class, the compiler doesn't know the type of the parameter you are using. This means that the generic parameter can be a reference type. This also means that the values or objects of the parameter could eventually hold null values. This would cause the compiler to issue a warning on a field or property created from the generic parameter. To prepare for this, you can apply the null-conditional operator on every field or property created from the parameter type. This can be done as follows:

public class Quadrilateral<T>
{
    protected T? _base;
    protected T? _height;
    protected string? _name;

    public virtual T? Base
    {
        get { return _base; }
        set { _base = value; }
    }

    public virtual T? Height
    {
        get { return _height; }
        set { _height = value; }
    }

    public virtual string? Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public Quadrilateral(string name = "Quadrilateral")
    {
        _name = name;
    }

    public Quadrilateral(T bs, T height)
    {
        _name = "Quadrilateral";
        _base = bs;
        _height = height;
    }

    public Quadrilateral(string name, T bs, T height)
    {
        _name = name;
        _base = bs;
        _height = height;
    }

    public virtual string Describe()
    {
        return "A quadrilateral is a geometric figure with four sides";
    }

    public virtual string ShowCharacteristics()
    {
        return $"Geometric Figure: {Name}, Description: {Describe()}, Base: {Base}, Height: {Height}";
    }
}

Deriving from a Generic Class

If you have a generic class that can serve as a foundation for another class, you can derive a class from the generic one. To do this, use the formula we apply when deriving a class but follow the name of each class with <>. Inside the <> operator, enter the same identifier to indicate that the class is a generic type that is based on another generic class. Here is an example:

public class Square<T> : Quadrilateral<T>
{
}

In the body of the new class, you can use the parameter type. For example, you can declare some member variables of that type. You can create methods that return the parameter type or you can pass arguments of the parameter type. When implementing the methods of the new class, use the member variables of the parameter and the argument(s) based on the parameter type. You can then declare a variable of the class and use it as we have done so far for other generic classes. Here is an example:

public class Quadrilateral<T>
{
    protected T? _base;
    protected T? _height;
    protected string? _name;

    public virtual T? Base
    {
        get { return _base; }
        set { _base = value; }
    }

    public virtual T? Height
    {
        get { return _height; }
        set { _height = value; }
    }

    public virtual string? Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public Quadrilateral(string name = "Quadrilateral")
    {
        _name = name;
    }

    public Quadrilateral(T bs, T height)
    {
        _name = "Quadrilateral";
        _base = bs;
        _height = height;
    }

    public Quadrilateral(string name, T bs, T height)
    {
        _name = name;
        _base = bs;
        _height = height;
    }

    public virtual string Describe()
    {
        return "A quadrilateral is a geometric figure with four sides";
    }

    public virtual string ShowCharacteristics()
    {
        return $"Geometric Figure: {Name}, Description: {Describe()}, Base: {Base}, Height: {Height}";
    }
}

public class Square<T> : Quadrilateral<T>
{
    public Square()
    {
        _name = "Square";
    }
    
    public Square(string name)
    {
        _name = "Square";
    }
    
    public Square(T side)
    {
        _name = "Square";
        _base = side;
        _height = side;
    }
    
    public Square(string name, T side)
    {
        _name = name;
        _base = side;
        _height = side;
    }
    
    public override string Describe()
    {
        return "A square is a quadrilateral with four equal sides";
    }
    
    public override string ShowCharacteristics()
    {
        return $"Geometric Figure: {Name}, Description: {Describe()}. {Describe()}, Side: {Base}";
    }
}

You can then use the class. Here is an example:

using static System.Console;

Title = "Geometry: Quadrilaterals";
WriteLine("Geometry: Quadrilaterals");
WriteLine("--------------------------------");

var plate = new Square<Byte>();

plate.Name = "Plate";
plate.Base = 15;
plate.Height = 28;

WriteLine(plate.ShowCharacteristics());
WriteLine("=================================");

public class Quadrilateral<T>
{
    protected T? _base;
    protected T? _height;
    protected string? _name;

    public virtual T? Base
    {
        get { return _base; }
        set { _base = value; }
    }

    public virtual T? Height
    {
        get { return _height; }
        set { _height = value; }
    }

    public virtual string? Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public Quadrilateral(string name = "Quadrilateral") => _name = name;

    public Quadrilateral(T bs, T height)
    {
        _name = "Quadrilateral";
        _base = bs;
        _height = height;
    }

    public Quadrilateral(string name, T bs, T height)
    {
        _name = name;
        _base = bs;
        _height = height;
    }

    public virtual string Describe() => "A quadrilateral is a geometric figure with four sides";

    public virtual string ShowCharacteristics() => $"Geometric Figure: {Name}, Description: {Describe()}, Base: {Base}, Height: {Height}";
}

public class Square<T> : Quadrilateral<T>
{
    public Square()
    {
        _name = "Square";
    }
        
    public Square(string name)
    {
        _name = "Square";
    }
        
    public Square(T side)
    {
        _name = "Square";
        _base = side;
        _height = side;
    }
        
    public Square(string name, T side)
    {
        _name = name;
        _base = side;
        _height = side;
    }
        
    public override string Describe()
    {
        return "A square is a quadrilateral with four equal sides";
    }
        
    public override string ShowCharacteristics()
    {
        return $"Geometric Figure: {Name}, Description: {Describe()}. {Describe()}, Side: {Base}";
    }
}

This would produce:

Geometry: Quadrilaterals
--------------------------------
Geometric Figure: Plate, Description: A square is a quadrilateral with four equa
l sides. A square is a quadrilateral with four equal sides, Side: 15
=================================
Press any key to close this window . . .

A Generic Interface

Introduction

If you are planning to create many generic classes, you can start or provide their common characteristics or behaviors in an interface.

Creating a Generic Interface

To create a generic interface, you primarily follow the rules for creating an interface except that you must add a parameter type. Here is an example:

public interface ICounter<T>
{
}

You should also add a (the) member(s) that the implementers will have to override. Here are examples of two members:

public interface ICounter<T>
{
    int Count { get; }
    T Get(int index);
}

In the same way, you can derive a generic interface from another generic interface. Here is an example:

public interface ICounter<T>
{
    int Count { get; }
    T Get(int index);
}

public interface IBuilder<T> : ICounter<T>
{
    void Add(T item);
}

Implementing a Generic Interface

After creating a generic interface, when deriving a class from it, follow the formula we reviewed for inheriting from a generic class. This means that the deriving class must use (a) parameter type(s). Here is an example:

public interface ICounter<T>
{
    int Count { get; }
    T Get(int index);
}

public interface IBuilder<T> : ICounter<T>
{
    void Add(T item);
}

public class People<T> : IPersons<T>
{
    
}

When implementing the derived class, you must observe all rules that apply to interface implementation. That is, you must implement all the members of the generic interface. Of course, you can also add new members if you want. Here is an example:

public interface ICounter<T>
{
    int Count { get; }
    T Get(int index);
}

public interface IBuilder<T> : ICounter<T>
{
    void Add(T item);
}

public class Associate<T> : IBuilder<T>
{
    private int size;
    private T[] objects;

    public Associate()
    {
        size = 0;
        objects = new T[10];
    }

	// This is a property implemented from the ICounter interface
    public int Count
    {
        get
        {
            return size;
        }
    }

    // This is a method implemented from the IBuilder interface
    public void Add(T pers)
    {
        objects[size] = pers;
        size++;
    }

    // This is a method implemented from the ICounter interface
    public T Get(int index)
    {
        return objects[index];
    }
}

After implementing the interface, you can declare a variable of the class and use it as you see fit. To do this, after the nane of the class, make sure you specify the parameter type between < and >. Initialize the variable appropriately. After that, you can acccess the members of the class. Here is an example:

using static System.Console;

Title = "Social Anthropology";
WriteLine("Social Anthropology");
WriteLine("------------------------------------------");

Associate<string> anthropology = new Associate<string>();

anthropology.Add("Convention");
anthropology.Add("Economics");
anthropology.Add("Politics");
anthropology.Add("Social Conflict");
anthropology.Add("Consumption");

WriteLine("The study of social anthropology includes:");

for (int i = 0; i < anthropology.Count; i++)
    WriteLine(anthropology.Get(i));
    
WriteLine("==========================================");

public interface ICounter<T>
{
    int Count { get; }
    T Get(int index);
}

public interface IBuilder<T> : ICounter<T>
{
    void Add(T item);
}

public class Associate<T> : IBuilder<T>
{
    private int size;
    private T[] objects;

    public Associate()
    {
        size = 0;
        objects = new T[10];
    }

    // This is a property implemented from the ICounter interface
    public int Count
    {
        get
        {
            return size;
        }
    }

    // This is a method implemented from the IBuilder interface
    public void Add(T pers)
    {
        objects[size] = pers;
        size++;
    }
        
    // This is a method implemented from the ICounter interface
    public T Get(int index)
    {
        return objects[index];
    }
}

This would produce:

Social Anthropology
------------------------------------------
The study of social anthropology includes:
Convention
Economics
Politics
Social Conflict
Consumption
==========================================
Press any key to close this window . . .

In the above example, we used a primitive type, namely a string, as a parameter type. Otherwise, you can use a class, either one of the many .NET Framework built-in classes or you can create your own.

Remember that you can declare the variable using either the var or the dynamic keyword. Here are examples:

using static System.Console;

var anthropology = new Associate<string>();
dynamic salaries = new Associate<decimal>();

anthropology.Add("Convention");
anthropology.Add("Economics");
anthropology.Add("Politics");
anthropology.Add("Social Conflict");
anthropology.Add("Consumption");
	
for(int i = 0; i < anthropology.Count; i++)
    anthropology.Get(i);

A Generic Interface as Parameter

A generic interface is primarily a normal interface like any other. It can be used to declare a variable but assigned the appropriate class. Here is an example:

using static System.Console;

Title = "Social Anthropology";
WriteLine("Social Anthropology");
WriteLine("------------------------------------------");

IBuilder<string> anthropology = new Associate<string>();

anthropology.Add("Convention");
anthropology.Add("Economics");
anthropology.Add("Politics");
anthropology.Add("Social Conflict");
anthropology.Add("Consumption");

WriteLine("The study of social anthropology includes:");

for (int i = 0; i < anthropology.Count; i++)
    WriteLine(anthropology.Get(i));
            
WriteLine("==========================================");

In the same way, a generic interface can be returned from a method. Here is an example:

public interface ICounter<T>
{
    int Count { get; }
    T Get(int index);
}

public interface IBuilder<T> : ICounter<T>
{
    void Add(T item);
}

public class Associate<T> : IBuilder<T>
{
    private int size;
    private T[] objects;

    public Associate()
    {
        size = 0;
        objects = new T[10];
    }

    public int Count { get { return size; } }

    public void Add(T pers)
    {
        objects[size] = pers;
        size++;
    }

    public T Get(int index) { return objects[index]; }
}

public class College
{
    public IBuilder<Course> CreateCourses()
    {
        Associate<Course> courses = new Associate<Course>();

        Course crs = new Course();
        crs.CourseName = "Online Research";
        crs.Credits = 1;
        courses.Add(crs);
        crs = new Course() { CourseName = "General Chemistry", Credits = 3 };
        courses.Add(crs);
        courses.Add(new Course() { CourseName = "Workplace Learning in Biology", Credits = 6 });
        courses.Add(new Course() { CourseName = "Linear Geometry 1", Credits = 4 });

        return courses;
    }
}

public record Course
{
    public string? CourseName { get; set; }
    public int Credits        { get; set; }
}

Here is an example of getting the returned generic value:

using static System.Console;

Title = "Social Anthropology";
WriteLine("Social Anthropology");
WriteLine("------------------------------------------");

College university = new College();
IBuilder<Course> studies = university.CreateCourses();

for (int i = 0; i < studies.Count; i++)
    WriteLine("{0}: {1} Credits", studies.Get(i).CourseName, studies.Get(i).Credits);
            
WriteLine("==========================================");

public interface ICounter<T>
{
    int Count { get; }
    T Get(int index);
}

public interface IBuilder<T> : ICounter<T>
{
    void Add(T item);
}

public class Associate<T> : IBuilder<T>
{
    private int size;
    private T[] objects;

    public Associate()
    {
        size = 0;
        objects = new T[10];
    }

    public int Count { get { return size; } }

    public void Add(T pers)
    {
        objects[size] = pers;
        size++;
    }

    public T Get(int index) { return objects[index]; }
}

public class College
{
    public IBuilder<Course> CreateCourses()
    {
        Associate<Course> courses = new Associate<Course>();

        Course crs = new Course();
        crs.CourseName = "Online Research";
        crs.Credits = 1;
        courses.Add(crs);
        crs = new Course() { CourseName = "General Chemistry", Credits = 3 };
        courses.Add(crs);
        courses.Add(new Course() { CourseName = "Workplace Learning in Biology", Credits = 6 });
        courses.Add(new Course() { CourseName = "Linear Geometry 1", Credits = 4 });

        return courses;
    }
}

public record Course
{
    public string? CourseName { get; set; }
    public int Credits        { get; set; }
}

This would produce:

Social Anthropology
------------------------------------------
Online Research: 1 Credits
General Chemistry: 3 Credits
Workplace Learning in Biology: 6 Credits
Linear Geometry 1: 4 Credits
==========================================
Press any key to close this window . . .

A generic interface can also be passed as argument. You pass a generic interface primarily the same way you would a regular interface. In the body of the method, you can ignore the argument or use it any way appropriate. Here is an example:

public class College
{
    public IBuilder<Course> CreateCourses()
    {
        Associate<Course> courses = new Associate<Course>();

        Course crs = new Course();
        crs.CourseName = "Online Research";
        crs.Credits = 1;
        courses.Add(crs);
        crs = new Course() { CourseName = "General Chemistry", Credits = 3 };
        courses.Add(crs);
        courses.Add(new Course() { CourseName = "Workplace Learning in Biology", Credits = 6 });
        courses.Add(new Course() { CourseName = "Linear Geometry 1", Credits = 4 });

        return courses;
    }

    public string Show(IBuilder<Course> values)
    {
        string strCourses = "";

        for (int i = 0; i <= 4; i++)
        {
            strCourses += values.Get(i).CourseName + "(" + values.Get(i).Credits + "), ";
        }

        strCourses = strCourses.Substring(0, strCourses.Length - 2);

        return strCourses;
    }
}

public record Course
{
    public string? CourseName { get; set; }
    public int Credits        { get; set; }
}

Here is an example of passing the generic object as argument:

using static System.Console;

Title = "Anthropology - College Courses";
WriteLine("Anthropology - College Courses");
WriteLine("---------------------------------------------------------------");
            
College university = new College();
IBuilder<Course> studies = new Associate<Course>();
            
studies.Add(new Course() { CourseName = "Business Startup", Credits = 1 });
studies.Add(new Course() { CourseName = "Introduction to Business and Management", Credits = 3 });
studies.Add(new Course() { CourseName = "Fundamentals of Digital Media", Credits = 3 });
studies.Add(new Course() { CourseName = "Predictive Modeling", Credits = 6 });
studies.Add(new Course() { CourseName = "Financial Management for Health Care Organizations", Credits = 3 });
            
string summary = university.Show(studies);

WriteLine(summary);

WriteLine("===============================================================");

public interface ICounter<T>
{
    int Count { get; }
    T Get(int index);
}

public interface IBuilder<T> : ICounter<T>
{
    void Add(T item);
}

public class Associate<T> : IBuilder<T>
{
    private int size;
    private T[] objects;

    public Associate()
    {
        size = 0;
        objects = new T[10];
    }

    public int Count { get { return size; } }

    public void Add(T pers)
    {
        objects[size] = pers;
        size++;
    }

    public T Get(int index) { return objects[index]; }
}

public class College
{
    public IBuilder<Course> CreateCourses()
    {
        Associate<Course> courses = new Associate<Course>();

        Course crs = new Course();
        crs.CourseName = "Online Research";
        crs.Credits = 1;
        courses.Add(crs);
        crs = new Course() { CourseName = "General Chemistry", Credits = 3 };
        courses.Add(crs);
        courses.Add(new Course() { CourseName = "Workplace Learning in Biology", Credits = 6 });
        courses.Add(new Course() { CourseName = "Linear Geometry 1", Credits = 4 });

        return courses;
    }

    public string Show(IBuilder<Course> values)
    {
        string strCourses = string.Empty;

        for (int i = 0; i <= 4; i++)
            strCourses += values.Get(i).CourseName + " (" + values.Get(i).Credits + " Credits)\n";

        strCourses = strCourses.Substring(0, strCourses.Length - 1);

        return strCourses;
    }
}

public record Course
{
    public string? CourseName { get; set; }
    public int    Credits     { get; set; }
}

This would produce:

Anthropology - College Courses
---------------------------------------------------------------
Business Startup (1 Credits)
Introduction to Business and Management (3 Credits)
Fundamentals of Digital Media (3 Credits)
Predictive Modeling (6 Credits)
Financial Management for Health Care Organizations (3 Credits)
===============================================================
Press any key to close this window . . .

A Generic Interface as a Parameter Type

A generic interface can be used as a parameter type. When creating a method, in its <> operator, specify the desired interface. A generic interface can also be used as the parameter type of an interface. As the number one rule for all methods that return a value, before exiting the method, you must return an object that is compatible with the generic interface. To do this, in the body of the method, you can declare a variable of a class that implements the interface, use that variable any appropriate way you want, and return it. Here is an example:

public interface ICounter<T>
{
    int Count { get; }
    T Get(int index);
}

public interface IBuilder<T> : ICounter<T>
{
    void Add(T item);
}

public interface IGovernment
{
    string? Continent { get; set; }
    string? Name { get; set; }
}

public class Country : IGovernment
{
    public string? Continent { get; set; }
    public string? Name { get; set; }
}

public class Associate<T> : IBuilder<T>
{
    private int size;
    private T[] objects;

    public Associate()
    {
        size = 0;
        objects = new T[10];
    }

    public int Count { get { return size; } }

    public void Add(T pers)
    {
        objects[size] = pers;
        size++;
    }

    public T Get(int index) { return objects[index]; }
}

public class Politics
{
    public Associate<IGovernment> Create()
    {
        IGovernment gov = new Country();
        gov.Continent = "Africa";
        gov.Name = "Sénégal";

        Associate<IGovernment> country = new Associate<IGovernment>();
        country.Add(gov);

        return country;
    }
}

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.

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:

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()

Introduction to Built-In Generic Interfaces and Classes

Overview

To assist you in creating generic classes, the .NET Framework provides a large library of generic interfaces. To assist you with various types of object, the .NET Framework provides a rich library of generic classes. The generic classes in the .NET Framework are extremely easy to use.

Enumerating a List

When using built-in classes, you will encounter some classes with a method named GetEnumerator. Normally, when you see a GetEnumerator() method, this means that its class is used to create or manage a list, such as an array. The immediate consequence is that you can use the foreach operator on the object (actually a list) of that class.

Spanning an Object

Introduction

To assist you in creating and controlling the memory area occupied by an array, the .NET Framework provides a built-in generic structure named Span

public readonly ref struct Span<T>

As is the case for practically all .NET built-in classes, the Span<T> structure is really easy to use it. To start, declare a variable of type Span<T>. Pass the type of the array as the parameter type. To help you in initializaing the variable, the Span<T> structure is equipped with four constructors. The most common constructor takes an array as argument:

public Span (T[]? array);

Using this constructor, you can pass an array as argument. You can create an array directly in the parentheses of the constructor. Here is an example:

Span<double> numbers = new Span<double>( new double[]{ 12.44, 7.137, 525.38, 46.28, 2448.32, 75.496, 632.04 } );

An Item of the Array

After the above declaration, the variable is an array and you can use it as such. For example, you can access a member of the array using the square brackets. To support this operation, the Span<T> structure is equipped with a this property:

public ref T this[int index] { get; }

Here is an example:

using static System.Console;

Span<double> numbers = new Span<double>( new double[]{ 12.44, 7.137, 525.38, 46.28, 2448.32, 75.496, 632.04 } );

WriteLine("Number: " + numbers[3].ToString());

WriteLine("===========================");

This would produce:

Number: 46.28

===========================

Press any key to close this window . . .

The Size of an Array

Like the Array class, the Span<T> structure is equipped with a property named Length:

public int Length { get; }

Enumerating an Array

The Span<T> structure is equipped with a GetEnumerator() method:

public Span<T>.Enumerator GetEnumerator ();

As a result, you can use a foreach operator to scan a Span<T> list. Here is an example:

using static System.Console;

Span<double> numbers = new Span<double>( new double[]{ 12.44, 8304.68, 7.137,  525.38,
                                                       46.28, 2448.32, 75.496, 632.04 } );

foreach (double number in numbers)
{
    WriteLine("Number: " + number.ToString());
}
WriteLine("===========================");

This would produce:

Number: 12.44
Number: 7.137
Number: 525.38
Number: 46.28
Number: 2448.32
Number: 75.496
Number: 632.04
===========================

Press any key to close this window . . .

Copying an Array

If you have an existing array and want to control how it is stored in memory, you can pass that array to a Span<T> variable. To perform this operation, you have two options. As one solution, you can pass the array variable to the above Span<T> constructor. This can be done as follows:

using static System.Console;

double[] values = { 12.44, 8304.68,  7.137, 525.38,
                    46.28, 2448.32, 75.496, 632.04 };

Span<double> numbers = new Span<double>(values);

foreach (double number in numbers)
{
    WriteLine("Number: " + number.ToString());
}
WriteLine("===========================");

Another solution is to directly assign an array to a Span<T> variable. This can be done as follows:

using static System.Console;

double[] values = { 12.44, 8304.68,  7.137, 525.38,
                    46.28, 2448.32, 75.496, 632.04 };

Span<double> numbers = values;

foreach (double number in numbers)
{
    WriteLine("Number: " + number.ToString());
}
WriteLine("===========================");

Slicing an Array

Slicing a list consists of creating a sub-list using a range of items from an existing list. To support this operation, the Span<T> structure is equipped with an overloaded method named Slice. One of the syntaxes of this method takes one integer argument:

public Span<T> Slice (int start);

This version specifies the index from which to start considering the members of the existing array. Another version takes two arguments:

public Span<T> Slice (int start, int length);

The first argument specifies from which index to start considering the items of the list. The second argument is the number of items from the starting index. Here is an example:

using static System.Console;

double[] values = { 12.44, 8304.68,  7.137, 525.38,
                    46.28, 2448.32, 75.496, 632.04 };

Span<double> numbers = values;

foreach (double number in numbers)
{
    WriteLine("Number: " + number.ToString());
}

WriteLine("--------------------------");

Span<double> part = numbers.Slice(2, 4);

foreach (double number in part)
{
    WriteLine("Number: " + number.ToString());
}
WriteLine("===========================");

This would produce:

Number: 12.44
Number: 8304.68
Number: 7.137
Number: 525.38
Number: 46.28
Number: 2448.32
Number: 75.496
Number: 632.04
--------------------------
Number: 7.137
Number: 525.38
Number: 46.28
Number: 2448.32
===========================

Press any key to close this window . . .

Fundamental Built-In Generic Classes

Introduction to the Collections Namespaces

The first public version of the .NET Framework was released as 1.1 in 2002. It contained a very impressive series of classes that could be used to create and manage collections. Most of the collection classes was created in the System.Collections namespace. The main goal was to provide all the routine operations performed on a list of items, whether a list of primitive values or a collection of composite objects. The flexibility of the new concept, as opposed to C++'s way of doing things, was the heavy use of interfaces. One of the classes introduced by the System.Collections namespaces is named ArrayList.

The 2.0 release of the .NET Framework saw the arrival of generic classes. The idea was to modernize the capabilities of existing collection classes. As a result, a new namespace was created and named System.Collections.Generics. Of course, to use a class from this namespace, make sure you qualify the name of its class or add the namespace in your using section:

using System.Collections.Generic;

public class Exercise
{
    public static int Main()
    {
        return 0;
    }
}

 A new equivalent version was created for most of the classes and interfaces of the System.Collections namespaces.

Introduction to the List Class

One of the classes introduced by the System.Collections.Generics namespace is named List. This is probably the most commonly used generic class of the .NET Framework. It is primarily the System.Collections.Generics equivalent of the System.Collections's ArrayList. It allows you to create a list of any kind of value.

Like any normal (non-static) class, to use a List object, you must first declare a variable for it. As a reminder of how to use a generic class, the formula to declare the variable is:

List<TypeName> VariableName = new List<TypeName>();

You can also declare the variable using the var keyword:

var VariableName = new List<TypeName>();

You can also use the dynamic keyword:

dynamic VariableName = new List<TypeName>();

These techniques use the default constructor of the List class. If the list is made of values of primitive types, put that value in the TypeName placeholder. Here are examples of List variables:

using System.Collections.Generic;

List<int> numbers = new List<int>();
var symbols = new List<char>();
dynamic names = new List<string>();

If the list is made of objects, make sure you specify it. You can use one of the existing classes of the .NET Framework. Here are examples:

using System.Collections.Generic;

List<EventArgs> eventArguments = new List<EventArgs>();
var enumerations = new List<Enum>();
dynamic exceptions = new List<Exception>();

Or you can create your own class. In both cases, the class must not be static.

Introduction to Using a List

Adding an Item to a List

The List class primarily supports all the necessary operations of a list. To allow you to add an item, the class is equipped with a method named Add. Its syntax is:

public void Add(T item)

To use it, simply pass the desired item to the method. If the value is a primitive type and if you want to pass a constant value, you must follow the rules of values of that type:

Here are examples:

using System.Collections.Generic;

List<int> numbers = new List<int>();
numbers.Add(248);
        
var symbols = new List<char>();
symbols.Add('%');

dynamic names = new List<string>();
names.Add("James");

Otherwise, you can first declare a variable for the value and pass the name of the variable to the List.Add() method.

If you are using a class, you must follow the rules of classes:

You can use the List.Add() method to add an item or to add many items one at a time. As an alternative, to let you add many items at the same time, the List class provides the AddRange() method.

Returning a Generic List From a Method

You can create a list in a method and return it. When defining the method, specify its return value as List<>. Inside the <> operator, write the type of value. Here is an example:

using System.Collections.Generic;

List<int> GetNumbers()
{
        
}

You can then define the method however you like, before exiting, you must make sure you return a List<> object. To use the list produced by the method, you can assign its return to local variable. Here is an example:

using System.Collections.Generic;

List<int> GetNumbers()
{
    List<int> nbrs = new List<int>();

    nbrs.Add(84);
    nbrs.Add(27407);
    nbrs.Add(5948);
    nbrs.Add(7);
    nbrs.Add(927482);

    return nbrs;
}

List<int> numbers = new List<int>();
numbers = GetNumbers();

Passing a Generic List as Argument

You can use a list either in a local method where it is created or you can pass it to be processed by another method. You pass it the same way you would any value, using List<TypeOfValue> ArgumentName. Here is an example:

using System.Collections.Generic;

void ShowNumbers(List<int> numbers)
{

}

In the method, you can use the argument any way you like.

The Number of Items in a List

To keep track of the number of items in a collection, the List class is equipped with a read-only property named Count:

public int Count { get; }

Here are examples of accessing it:

public class Exercise
{
    public static int Main()
    {
        List<Employee> contractors = new List<Employee>();

        Employee empl = new Employee(397947, "David", "Redson", 18.75);
        contractors.Add(empl);
        empl = new Employee(840004, "Simao");
        contractors.Add(empl);
        contractors.Add(new Employee());
        
        var seasonals = new List<Employee>();
        seasonals.Add(new Employee(848024, "Alima", "Bieyrou", 14.05));
        seasonals.Add(new Employee(fName : "Robert", lName : "Nants"));

        dynamic identifications = new List<Employee>();
        identifications.Add(new Employee(222222));

        Console.WriteLine("This company has {0} contractors, {1} seasonal " +
                          "employees, and {2} other type.", contractors.Count,
                          seasonals.Count, identifications.Count);
                          
        return 0;
    }
}

This would produce:

This company has 3 contractors, 2 seasonal employees, and 1 other type.
Press any key to close this window . . .

The Capacity of a List

When you declare a List variable, the compiler reserves a certain area and amount of memory for it. If you want, you can tell the compiler how much initial memory to reserve. To support this, the List class is equipped with a property named Capacity:

public int Capacity { get; set; }

To specify the initial capacity when declaring the List variable, you can use the following constructor:

public List(int capacity);

On the other hand, to get the capacity of a List variable, get the variable of its Capacity.

Accessing an Item

To let you access an item, the List class uses an indexed property named Item. This allows you to access its items using the [] operator. Here are examples:

using System.Collections.Generic;

public class Exercise
{
    public static int Main()
    {
        var name = new List<char>();
        name.Add('A');
        name.Add('p');
        name.Add('l');
        name.Add('e');

        Console.Write("{0}", name[0]);
        Console.Write("{0}", name[1]);
        Console.Write("{0}", name[1]);
        Console.Write("{0}", name[2]);
        Console.Write("{0}", name[3]);
        Console.WriteLine();

        return 0;
    }
}

This would produce:

Apple
Press any key to close this window . . .

You can also use a for loop to access each item of the list. Here is an example:

using System.Collections.Generic;

public class Exercise
{
    public static int Main()
    {
        var internetCode = new List<char>();
        internetCode.Add('c');
        internetCode.Add('o');
        internetCode.Add('m');

        Console.Write("http://www.kamerun.");
        for(int i = 0; i < internetCode.Count; i++)
            Console.Write("{0}", internetCode[i]);

        Console.WriteLine();
        return 0;
    }
}

This would produce:

http://www.kamerun.com
Press any key to close this window . . .

If the list is made of objects of a class, to access an item, apply the square brackets to the variable, followed by a period, and followed by the desired member of the class. Here are examples:

public class Exercise
{
    public static int Main()
    {
        List<Employee> contractors = new List<Employee>();

        Employee empl = new Employee(397947, "David", "Redson", 18.75);
        contractors.Add(empl);
        contractors.Add(new Employee(174966, "Alfred", "Swanson", 12.94));
        empl = new Employee(840004, "Simao");
        contractors.Add(empl);
        contractors.Add(new Employee(848024, "Alima", "Bieyrou", 14.05));
        contractors.Add(new Employee(number: 397462,
        			     fName : "Robert", 
        			     lName : "Nants"));

        dynamic identifications = new List<Employee>();
        identifications.Add(new Employee(222222));

        for (int i = 0; i < contractors.Count; i++)
        {
            Console.WriteLine("Employee #:    {0}\nFirst Name:    {1}\n" +
                              "Last Name:     {2}\n" +
                              "Hourly Salary: {3:F}",
                              contractors[i].EmployeeNumber,
                              contractors[i].FirstName,
                              contractors[i].LastName,
                              contractors[i].HourlySalary);
            Console.WriteLine("-----------------------");
        }
                          
        return 0;
    }
}

This would produce:

Employee #:    397947
First Name:    David
Last Name:     Redson
Hourly Salary: 18.75
-----------------------
Employee #:    174966
First Name:    Alfred
Last Name:     Swanson
Hourly Salary: 12.94
-----------------------
Employee #:    840004
First Name:    Simao
Last Name:     Doe
Hourly Salary: 12.00
-----------------------
Employee #:    848024
First Name:    Alima
Last Name:     Bieyrou
Hourly Salary: 14.05
-----------------------
Employee #:    397462
First Name:    Robert
Last Name:     Nants
Hourly Salary: 12.00
-----------------------
Press any key to close this window . . .

Enumerating a List

 

Introduction

In previous sections, we saw that you can use a for loop to scan a list of items. To support the foreach operator, the List class is equipped with a method named GetEnumerator. Its syntax is:

public List<(Of <(<'T>)>)>..::..Enumerator GetEnumerator();

Normally, you don't need to know anything about this method. You use foreach the same was we saw for arrays. Here is an example:

public class Exercise
{
    public static int Main()
    {
        List<Employee> employees = new List<Employee>();

        Employee empl = new Employee(397947, "David", "Redson", 18.75);
        employees.Add(empl);
        employees.Add(new Employee(174966, "Alfred", "Swanson", 12.94));
        employees.Add(new Employee(848024, "Alima", "Bieyrou", 14.05));
        employees.Add(new Employee(number: 397462, fName: "Robert",
                                     lName: "Nants", salary: 22.15));

        foreach (Employee staffMember in employees)
        {
            Console.WriteLine("================================");
            Console.WriteLine("Employee Record");
            Console.WriteLine("--------------------------------");
            Console.WriteLine("Employee #:    {0}", staffMember.EmployeeNumber);
            Console.WriteLine("First Name:    {0}", staffMember.FirstName);
            Console.WriteLine("Last Name:     {0}", staffMember.LastName);
            Console.WriteLine("Hourly Salary: {0}", staffMember.HourlySalary);
        }

        return 0;
    }
}

For Each Method Call

The List class provides its own method that performs the operation of foreach. Its name is ForEach and its syntax is:

public void ForEach(Action<T> action);

This method expects a function that defines what to do every time the compiler visits a members of the list. You do this by creating a method to do something on each member of the List<> collection and then pass that method to ForEach(). Here is an example:

public class Exercise
{
    public void ShowEmployee(Employee empl)
    {
        Console.WriteLine("================================");
        Console.WriteLine("Employee Record");
        Console.WriteLine("--------------------------------");
        Console.WriteLine("Employee #:    {0}", empl.EmployeeNumber);
        Console.WriteLine("First Name:    {0}", empl.FirstName);
        Console.WriteLine("Last Name:     {0}", empl.LastName);
        Console.WriteLine("Hourly Salary: {0}", empl.HourlySalary);
    }

    public static int Main()
    {
        Exercise exo = new Exercise();
        List<Employee> employees = new List<Employee>();

        Employee empl = new Employee(397947, "David", "Redson", 18.75);
        employees.Add(empl);
        employees.Add(new Employee(174966, "Alfred", "Swanson", 12.94));
        employees.Add(new Employee(848024, "Alima", "Bieyrou", 14.05));
        employees.Add(new Employee(number: 397462, fName: "Robert",
                                   lName: "Nants", salary: 22.15));

        employees.ForEach(exo.ShowEmployee);
        
        return 0;
    }
}

Looking for an Item

  

Introduction

The List class provides various means of finding out whether an item exists in a list. One of the methods you can use is named Contains. Its syntax is:

public bool Contains(T item);

This methods takes as argument the item to look for. If that item exists in the list, the method returns true. Otherwise, it returns false. This method is easy to use on a list that contains value of primitive types. Here is an example:

using System.Collections.Generic;

public class Exercise
{
    public static int Main()
    {
        var connection = new List<char>();
        connection.Add('c');
        connection.Add('o');
        connection.Add('n');
        connection.Add('n');
        connection.Add('e');
        connection.Add('c');
        connection.Add('t');
        connection.Add('i');
        connection.Add('o');
        connection.Add('n');

        for (int i = 0; i < connection.Count; i++)
            Console.Write("{0}", connection[i]);
        Console.WriteLine();

        if (connection.Contains('e'))
            Console.WriteLine("The list contains 'e'");
        else
            Console.WriteLine("There is no 'e' in the list");

        if (connection.Contains('g'))
            Console.WriteLine("The list contains 'g'");
        else
            Console.WriteLine("There is no 'g' in the list");

        return 0;
    }
}

This would produce:

connection
The list contains 'e'
There is no 'g' in the list
Press any key to close this window . . .

This works because each structure of a primitive type is equipped to compare two of its values. The comparison would not work directly if the list is made of values of your own class. Consider the following:

using System.Collections.Generic;

public class Employee
{
    public long EmployeeNumber { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public double HourlySalary { get; set; }

    public Employee(long number = 0, string fName = "John",
                    string lName = "Doe", double salary = 12.05D)
    {
        EmployeeNumber = number;
        FirstName = fName;
        LastName = lName;
        HourlySalary = salary;
    }

    public override string ToString()
    {
        base.ToString();

        return string.Format("================================\n" +
                             "Employee Record\n" +
                             "--------------------------------\n" +
                             "Employee #:    {0}\nFirst Name:    {1}\n" +
                             "Last Name:     {2}\nHourly Salary: {3}",
                             EmployeeNumber, FirstName,
                             LastName, HourlySalary);
    }
}

public class Exercise
{
    public static int Main()
    {
        List<Employee> employees = new List<Employee>();

        Employee empl = new Employee(397947, "David", "Redson", 18.75);
        employees.Add(empl);
        employees.Add(new Employee(174966, "Alfred", "Swanson", 12.94));
        employees.Add(new Employee(848024, "Alima", "Bieyrou", 14.05));
        employees.Add(new Employee(number: 397462, fName: "Robert",
                                     lName: "Nants", salary: 22.15));

        Employee member = new Employee(174966, "Alfred", "Swanson", 12.94);

        bool exists = employees.Contains(member);

        if (exists == true)
            Console.WriteLine("The list contains the employee");
        else
            Console.WriteLine("No record of that employee was found");

        return 0;
    }
}

This would produce:

No record of that employee was found
Press any key to close this window . . .

Notice that the compiler was not able to find the item even though it actually exists in the list. To make this method work on your class, you must override the Equals() method. Here is an example:

using System.Collections.Generic;

public class Employee
{
    public long EmployeeNumber { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public double HourlySalary { get; set; }

    public Employee(long number = 0, string fName = "John",
                    string lName = "Doe", double salary = 12.05D)
    {
        EmployeeNumber = number;
        FirstName = fName;
        LastName = lName;
        HourlySalary = salary;
    }

    public override bool Equals(object obj)
    {
        Employee e = (Employee)obj;

        if ((e.EmployeeNumber == this.EmployeeNumber) &&
            (e.FirstName == this.FirstName) &&
            (e.LastName == this.LastName) &&
            (e.HourlySalary == this.HourlySalary))
            return true;
        else
            return false;
    }

    public override string ToString()
    {
        base.ToString();

        return string.Format("================================\n" +
                             "Employee Record\n" +
                             "--------------------------------\n" +
                             "Employee #:    {0}\nFirst Name:    {1}\n" +
                             "Last Name:     {2}\nHourly Salary: {3}",
                             EmployeeNumber, FirstName,
                             LastName, HourlySalary);
    }
}

public class Exercise
{
    public static int Main()
    {
        . . . No Change

        return 0;
    }
}

This time, the program would produce:

The list contains the employee
Press any key to close this window . . .

Getting the Index of Item

You can ask the compiler to look for an item in the list and, if it finds it, to give you the index of that item. To make this request, use the IndexOf() method. It is overloaded with three versions. One of them uses the following syntax:

public int IndexOf(T item);

When calling this method, if the list is made of values of primitive types, pass the value as argument. Here is an example:

using System.Collections.Generic;

public class Exercise
{
    public static int Main()
    {
        var connection = new List<char>();
        
        connection.Add('c');
        connection.Add('o');
        connection.Add('n');
        connection.Add('n');
        connection.Add('e');
        connection.Add('c');
        connection.Add('t');
        connection.Add('i');
        connection.Add('o');
        connection.Add('n');

        for (int i = 0; i < connection.Count; i++)
            Console.Write("{0}", connection[i]);
        Console.WriteLine();

        Console.WriteLine("The index of 'n' is {0}", connection.IndexOf('n'));

        return 0;
    }
}

This would produce:

connection
The index of 'n' is 2
Press any key to close this window . . .

As mentioned for the Contains() method, when you call the IndexOf() method, the compiler compares each member of the collection with the value passed as argument. This works fine on value of primitive types because they have built-in comparison in their structures. If you want this to work on a class you created, you must override the Equals() method.

If you call the above version of the IndexOf() method, the compiler would look for the item in the entire list starting from the beginning, the 0-index. If you want, you can ask the compiler to start looking at a specific position. To do this, use the following version of the IndexOf() method:

public int IndexOf(T item, int index);

This time, besides the item, pass an index as the second argument. The compiler would look for the item starting at that index to the end.

When you use the IndexOf() method, the compiler would look for the item starting from the left to the right. If you prefer to look for the item from the end of the list, you can use the LastIndexOf() method. It is overloaded with three versions. One of them uses a syntax as:

public int LastIndexOf(T item);

Here is an example of calling this method:

using System.Collections.Generic;

public class Exercise
{
    public static int Main()
    {
        var connection = new List<char>();
        
        connection.Add('c');
        connection.Add('o');
        connection.Add('n');
        connection.Add('n');
        connection.Add('e');
        connection.Add('c');
        connection.Add('t');
        connection.Add('i');
        connection.Add('o');
        connection.Add('n');

        for (int i = 0; i < connection.Count; i++)
            Console.Write("{0}", connection[i]);
        Console.WriteLine();

        Console.WriteLine("The index of 'n' from the end to the beginning is {0}",
                          connection.LastIndexOf('n'));

        return 0;
    }
}

This would produce:

connection
The index of 'n' from the end to the beginning is 9
Press any key to close this window . . .

If you prefer to look for the item starting at a certain index from the end, use the following syntax:

public int LastIndexOf(T item, int index);

Deleting Items

 

Deleting an Item Using its Index

There are various ways you can remove an item from a list. To let you delete an item based on its index, you can call the RemoveAt() method. Its syntax is:

public void RemoveAt(int index);

When calling this method, pass the index of the undesired item. Here is an example:

using System.Collections.Generic;

public class Exercise
{
    public static int Main()
    {
        var internetCode = new List<char>();
        internetCode.Add('c');
        internetCode.Add('o');
        internetCode.Add('m');

        Console.Write("http://www.kamerun.");
        for(int i = 0; i < internetCode.Count; i++)
            Console.Write("{0}", internetCode[i]);
        Console.WriteLine();

        internetCode.RemoveAt(1);

        Console.Write("http://www.kamerun.");
        for (int i = 0; i < internetCode.Count; i++)
            Console.Write("{0}", internetCode[i]);
        Console.WriteLine();

        return 0;
    }
}

This would produce:

http://www.kamerun.com
http://www.kamerun.cm
Press any key to close this window . . .

As you can see, the RemoveAt() method is the easy way to delete an item from a list if you know its index. Another way you can proceed would consist of finding out first if the item exists in the list, get its index, and then pass that index.

Deleting a Range of Items

Still using an index, but instead of deleting just one, you can remove a range of value from a list. To support this, the List<> class provides the RemoveRange() method. Its syntax is:

public void RemoveRange(int index, int count);

The first item is the index from where to start deleting items. The second argument specifies the number of items to delete from the index to the end of the list.

Deleting a Known Item

Instead of locating an item based on its index, you can ask the List class to look for it. To support this, it is equipped with a method named Remove. Its syntax is:

public bool Remove(T item);

In this case, you pass a definition of the item to the method. Here is an example:

using System.Collections.Generic;

public class Exercise
{
    public static int Main()
    {
        var internetCode = new List<char>();
        internetCode.Add('c');
        internetCode.Add('o');
        internetCode.Add('m');

        Console.Write("http://www.kamerun.");
        for(int i = 0; i < internetCode.Count; i++)
            Console.Write("{0}", internetCode[i]);
        Console.WriteLine();

        internetCode.Remove('o');

        Console.Write("http://www.kamerun.");
        for (int i = 0; i < internetCode.Count; i++)
            Console.Write("{0}", internetCode[i]);
        Console.WriteLine();

        return 0;
    }
}

When you call the List.Remove() method, the compiler would look for the item in the collection:

Notice that the list contains three Ns but only the first was removed.

If the item is based on a class, you must (it is your responsibility, the List class it not equipped to) provide a way to identify an item. This is usually done by overriding the Equals() method.

Clearing the List

To delete all items from a list, call the Clear() method. Its syntax is:

public void Clear();

Other Operations on a List

 

Creating a Sub-List

If you have an existing list, you can use a range of indices from it to retrieve a new list. To support this operation, the List class provides the GetRange() method whose syntax is:

public List<T> GetRange(int index, int count);

The first argument of this method is the start point of the range. The second argument is the number of items from the index to include in the new list. Here are examples of calling this method:

using System.Collections.Generic;

public class Exercise
{
    public static int Main()
    {
        var connection = new List<char>();
        
        connection.Add('c');
        connection.Add('o');
        connection.Add('n');
        connection.Add('n');
        connection.Add('e');
        connection.Add('c');
        connection.Add('t');
        connection.Add('i');
        connection.Add('o');
        connection.Add('n');

        for (int i = 0; i < connection.Count; i++)
            Console.Write("{0}", connection[i]);
        Console.WriteLine();

        List<char> connect = connection.GetRange(0, 7);

        for (int i = 0; i < connect.Count; i++)
            Console.Write("{0}", connect[i]);
        Console.WriteLine();

        List<string> person = new List<string>();
        person.Add("Paul");
        person.Add("Martin");
        person.Add("Souffrance");
        person.Add("Eto");
        person.Add("Zambo");

        for (int i = 0; i < person.Count; i++)
            Console.Write("{0} ", person[i]);
        Console.WriteLine();

        List<string> pers = person.GetRange(1, 2);

        for (int i = 0; i < pers.Count; i++)
            Console.Write("{0} ", pers[i]);
        Console.WriteLine();

        return 0;
    }
}

This would produce:

connection
connect
Paul Martin Souffrance Eto Zambo
Martin Souffrance
Press any key to close this window . . .

In the same way, you can create a sub-list of objects from an existing collection.

Converting a List Into an Array

To convert a list into an array, you can call the ToArray() method of the List<> class. Its syntax is:

public T[] ToArray();

Here is an example of calling this method:

public class Exercise
{
    public static int Main()
    {
        List<Employee> contractors = new List<Employee>();

        Employee empl = new Employee(397947, "David", "Redson", 18.75);
        contractors.Add(empl);
        contractors.Add(new Employee(174966, "Alfred", "Swanson", 12.94));
        contractors.Add(new Employee(405809, "Amie", "Tripp", 16.55));
        contractors.Add(new Employee(294815, "Theodore", "Ludlum", 8.05));
        contractors.Add(new Employee(848024, "Alima", "Bieyrou", 14.05));
        contractors.Add(new Employee(number: 397462, fName: "Robert",
                                     lName: "Nants", salary : 22.15));

        Employee[] employees = contractors.ToArray();

        foreach (Employee staff in employees)
            Console.WriteLine(staff);

        return 0;
    }
}

Previous Copyright © 2008-2024, FunctionX Tuesday 19 October 2021 Next