Sunday, June 28, 2026

C# How to use private method implemented in an interface type

We know that we cannot use private member outside its own type. The private member can be consumed only inside its own type. This is true for interface as well. Let us consider the following code. Since Print method is not consumed inside ICommon interface, it is useless. Then how to use a private method inside an interface? We can use private method as a helper method.
interface ICommon
{
    private void Print()
    {
        Console.WriteLine("C#8 onward access modifier allowed.");
    }
}

class Test : ICommon { }
class Program
{
    static void Main(string[] args)
    {
        Test test = new Test();
    }
}
Example
We can create private method inside an interface as a helper method. 
interface ILogger
{
    void LogInfo(string msg)
    {
        Write("INFO", msg);
    }

    void LogError(string msg)
    {
        Write("ERROR", msg);
    }

    // shared helper
    private void Write(string type, string msg)
    {
        Console.WriteLine($"[{type}] {msg}");
    }
}

class MyLogger : ILogger { }

class Program
{
    static void Main()
    {
        ILogger logger = new MyLogger();

        logger.LogInfo("Started");
        logger.LogError("Failed");
    }
}
OUTPUT
[INFO] Started
[ERROR] Failed

C# Comparison of IComparer with generic IComparer type

The generic IComparer<T> is type safe compared to nongeneric IComparer. A separate class is used to compare property or properties of type T. In the following example, names of employees are compared which helps to sort the employees by names.
class Employee
{
    public string? Name { get; set; }
    public int Age { get; set; }
}

// IComparer<Employee> is used to compare 2 Employee objects
// IComparer<string> is used to compare strings
// Since, you have to compare names of 2 employees
// hence, IComparer<Employee> is valid
class EmployeeNameComparer : IComparer<Employee>
{
    public int Compare(Employee? x, Employee? y)
    {
        if (x != null && y != null)
        {
            return string.Compare(x.Name, y.Name, StringComparison.Ordinal);
        }
        else
        {
            return 0;
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Employee[] employees = {
            new Employee { Name = "Rajesh", Age = 21 } ,
            new Employee { Name = "Ajay", Age = 23 },
            new Employee { Name="Niraj",Age=31}
        };
        Console.WriteLine("---Before Sorting---");
        foreach (var employee in employees)
        {
            Console.WriteLine($"Name: {employee.Name} Age:{employee.Age}");
        }
        Array.Sort<Employee>(employees, new EmployeeNameComparer());
        Console.WriteLine("---After Sorting---");
        foreach (var employee in employees)
        {
            Console.WriteLine($"Name: {employee.Name} Age:{employee.Age}");
        }
    }
}
OUTPUT
---Before Sorting---
Name: Rajesh Age:21
Name: Ajay Age:23
Name: Niraj Age:31
---After Sorting---
Name: Ajay Age:23
Name: Niraj Age:31
Name: Rajesh Age:21

The nongeneric IComparer example is given in another post. Click here.

The generic IComparer<in T> and Contravariance
Since the generic IComparer<T> supports contravariance, we can use more derived type also. For example, IComparer<Employee> can be substituted by IComparer<Manager> where Manager is derived class from Employee class.

// This compiles successfully ONLY because of contravariance (the 'in' keyword)
IComparer<Manager> managerComparer = new EmployeeNameComparer();

Since IComparer<in T> by definition supports contravariance. It means if I have IComparer<Employee> implemented by EmployeeNameComparer then IComparer<Manager> need not be again implemented by some other class. Same class EmployeeNameComparer can be used to sort managers name.
Example.
class Employee
{
    public string? Name { get; set; }
    public int Age { get; set; }
}
class Manager : Employee
{
    public int Salary { get; set; }
}

// IComparer<Employee> is used to compare 2 Employee objects
// IComparer<string> is used to compare strings
// Since, you have to compare names of 2 employees
// hence, IComparer<Employee> is valid
class EmployeeNameComparer : IComparer<Employee>
{
    public int Compare(Employee? x, Employee? y)
    {
        if (x != null && y != null)
        {
            return string.Compare(x.Name, y.Name, StringComparison.Ordinal);
        }
        else
        {
            return 0;
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Manager[] managers = {
            new Manager{ Name = "Rajesh", Age = 21 } ,
            new Manager{ Name = "Ajay", Age = 23 },
            new Manager{ Name="Niraj",Age=31}
        };
        Console.WriteLine("---Before Sorting---");
        foreach (var manager in managers)
        {
            Console.WriteLine($"Name: {manager.Name} Age:{manager.Age}");
        }
        Array.Sort<Employee>(managers, new EmployeeNameComparer());
        Console.WriteLine("---After Sorting---");
        foreach (var manager in managers)
        {
            Console.WriteLine($"Name: {manager.Name} Age:{manager.Age}");
        }
    }
}
Point to Note.
In C#, arrays of reference types are covariant. This means that a Manager[] can be implicitly cast to an Employee[]. When you call: Array.Sort(managers, new EmployeeNameComparer()); The C# compiler does not see a type mismatch because it implicitly cast Manager[] array into an Employee[]. It treats the array as a collection of employees, sorts them using Employee comparer, and because they are still Manager objects under the hood, it works flawlessly at runtime!

Hot Topics