נושאים מתקדמים ב-C#

ההבדל בין תכונות הבסיס של C# והאפשרויות המתקדמות יותר נובע מרמת המורכבות ורמת ההפשטה של הקוד.

נפתח פרוייקט חדש Console App (.Net Framework). נקרא לו SchoolHRAdministration. בתוך הפרוייקט נפתח פרוייקט של Class Library. נקרא לו HRAdministrationApi. נוסיף לפרוייקט interface בשם IEmployee.

הגדרת Interface

namespace HRAdministrationApi
{
    public interface IEmployee
    {
        int Id { get; set; }
        string FirstName { get; set; }
        string LastName { get; set; }
        decimal Salary { get; set; }
    }
}

ניצור class ונקרא לו EmployeeBase.

public class EmployeeBase : IEmployee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public virtual decimal Salary { get; set; }
}

נחזור ל-SchoolHRAdministration וניצור מספר מחלקות.

קובץ program.cs

namespace SchoolHRAdministration
{
    class Program
    {
        static void Main(string[] args) 
        {
        }
    }

    public class Teacher : EmployeeBase
    {

    }

    public class HeadOfDepartment : EmployeeBase
    {

    }

    public class DeputyHeadMaster : EmployeeBase
    {

    }

    public class HeadMaster: EmployeeBase
    {

    }
}

נניח שיש איזה נוסחא לחישוב במשכורת השנתית של העובדים כולל בונוסים. ניצור פונקציה ובה 5 מופעים של עובדים. הנתונים האלה בד"כ יגיעו מ-DB.

class Program
{
    static void Main(string[] args) 
    {
           
    }

    public static void SeedData(List<IEmployee> employee)
    {
        IEmployee teacher1 = new Teacher
        {
            Id = 1,
            FirstName = "Bob",
            LastName = "Fisher",
            Salary = 40000
        };
        employee.Add(teacher1);

        IEmployee teacher2 = new Teacher
        {
            Id = 2,
            FirstName = "Jenny",
            LastName = "Thomas",
            Salary = 40000
        };
        employee.Add(teacher2);

        IEmployee headOfDepatment = new HeadOfDepartment
        {
            Id = 3,
            FirstName = "Jack",
            LastName = "White",
            Salary = 50000
        };
        employee.Add(headOfDepatment);

        IEmployee deputyHeadMaster = new DeputyHeadMaster
        {
            Id = 4,
            FirstName = "Devlin",
            LastName = "Brown",
            Salary = 60000
        };
        employee.Add(deputyHeadMaster);

        IEmployee headMaster = new HeadMaster
        {
            Id = 5,
            FirstName = "Danny",
            LastName = "Jones",
            Salary = 80000
        };
        employee.Add(headMaster); 
    }
}

נניח שמשלמים בונוס לכל חבר צוות שנתית לפי הותק. נדרוס את פונקציית המשכורת עבור כל מחלקה ונוסיף את הבונוס למשכורת.

public class Teacher : EmployeeBase
{
    public override decimal Salary { get => base.Salary + (base.Salary * 0.02m); }
}

public class HeadOfDepartment : EmployeeBase
{
    public override decimal Salary { get => base.Salary + (base.Salary * 0.03m); }
}

public class DeputyHeadMaster : EmployeeBase
{
    public override decimal Salary { get => base.Salary + (base.Salary * 0.04m); }
}

public class HeadMaster: EmployeeBase
{
    public override decimal Salary { get => base.Salary + (base.Salary * 0.05m); }
}

עכשיו אנחנו רוצים לחשב את המשכורות של הצוות כולל בונוסים. אפשר לעשות את זה על ידי לולאה בתוכנית הראשית.

static void Main(string[] args) 
{
    decimal totalSalaries = 0;
    List<IEmployee> lstEmployee = new List<IEmployee>();

    SeedData(lstEmployee);

    foreach(IEmployee employee in lstEmployee) {
        totalSalaries += employee.Salary;
    }

    Console.WriteLine($"Total: {totalSalaries}");
    Console.ReadLine();
}

שימוש בספריית Linq

אפשר לבצע את הפעולה הזאת הצורה פשוטה יותר.

using System.Linq;

// Instead of the foreach
Console.WriteLine($"Total: {lstEmployee.Sum(e => e.Salary)}");

יש לנו שימוש בפונקציית sum ששייכת לספריית Linq. הפונקציה עושה שימוש בפונקציית למדא שמקבלת את e ומוציאה ערך דצימלי.

איתחול אובייקטים עם Factory Class

ניצור קודם enum עם סוגי התפקידים שיש לנו. ואז את ה-class שמייצר את הסוגים השונים של העובדים.

public enum EmployeeType
{
    Teacher,
    HeadOfDepartment,
    DeputyHeadMaster,
    HeadMaster
}

public static class EmployeeFactory
{
    public static IEmployee GetEmployeeInstance(
        EmployeeType employeeType, int id, string firstName, string lastName, decimal salary)
    {
        IEmployee employee = null;

        switch (employeeType) {
            case EmployeeType.Teacher:
                employee = new Teacher { Id = id, FirstName = firstName, 
                           LastName = lastName, Salary = salary };
                break;
            case EmployeeType.HeadOfDepartment:
                employee = new HeadOfDepartment { Id = id, FirstName = firstName, 
                           LastName = lastName, Salary = salary };
                break;
            case EmployeeType.DeputyHeadMaster:
                employee = new DeputyHeadMaster { Id = id, FirstName = firstName, 
                           LastName = lastName, Salary = salary };
                break;
            case EmployeeType.HeadMaster:
                employee = new HeadMaster { Id = id, FirstName = firstName, 
                           LastName = lastName, Salary = salary };
                break;
            default:
                break;
        }

        return employee;
    }
}

עכשיו נשנה את פונקציית SeedData כך שתשתמש ב-class החדש שיצרנו על מנת לייצר עובדים.

public static void SeedData(List<IEmployee> employees)
{
    IEmployee teacher1 = EmployeeFactory.GetEmployeeInstance(EmployeeType.Teacher, 1, "Bob", "Fisher", 40000);
    employees.Add(teacher1);

    IEmployee teacher2 = EmployeeFactory.GetEmployeeInstance(EmployeeType.Teacher, 2, "Jenny", "Thomas", 40000);
    employees.Add(teacher2);

    IEmployee headOfDepatment = EmployeeFactory.GetEmployeeInstance(EmployeeType.HeadOfDepartment, 3, "Jack", "White", 50000);
    employees.Add(headOfDepatment);

    IEmployee deputyHeadMaster = EmployeeFactory.GetEmployeeInstance(EmployeeType.DeputyHeadMaster, 4, "Devlin", "Brown", 60000);
    employees.Add(deputyHeadMaster);

    IEmployee headMaster = EmployeeFactory.GetEmployeeInstance(EmployeeType.HeadMaster, 5, "Danny", "Jones", 80000);
    employees.Add(headMaster); 
}

יצרנו את העובדים בצורה אבסטרקטית והקוד עובד אותו הדבר.

Generic Factory

נלך עוד צעד קדימה ונבנה class שהוא factory בתוך ה-HRAdministrationApi. נקרא לו FactoryPattern.

public static class FactoryPattern<K, T> where T:class, K, new()
{
    public static K GetInstance()
    {
        K objK;
        objK = new T();
        return objK;
    }
}

נחזור לקוד שבו ייצרנו את העובדים לפי הסוג שלהם ונעדכן את היצירה של העובדים עם הפונקציה הגנרית שיצרנו. ייצור העובדים מופשט יותר משהיה קודם.

public static class EmployeeFactory
{
    public static IEmployee GetEmployeeInstance(EmployeeType employeeType, int id, string firstName, string lastName, decimal salary)
    {
        IEmployee employee = null;

        switch (employeeType) {
            case EmployeeType.Teacher:
                employee = FactoryPattern<IEmployee, Teacher>.GetInstance();
                break;
            case EmployeeType.HeadOfDepartment:
                employee = FactoryPattern<IEmployee, HeadOfDepartment>.GetInstance();
                break;
            case EmployeeType.DeputyHeadMaster:
                employee = FactoryPattern<IEmployee, DeputyHeadMaster>.GetInstance(); 
                break;
            case EmployeeType.HeadMaster:
                employee = FactoryPattern<IEmployee, HeadMaster>.GetInstance(); 
                break;
            default:
                break;
        }

        if(employee != null) {
            employee.Id = id;
            employee.FirstName = firstName;
            employee.LastName = lastName;
            employee.Salary = salary;
        } else {
            throw new NullReferenceException();
        }

        return employee;
    }
}