C# - Interface

In the human world, a contract between the two or more humans binds them to act as per the contract. In the same way, an interface includes the declarations of related functionalities. The entities that implement the interface must provide the implementation of declared functionalities.

In C#, an interface can be defined using the interface keyword. An interface can contain declarations of methods, properties, indexers, and events. However, it cannot contain fields, auto-implemented properties.

The following interface declares some basic functionalities for the file operations.

Example: C# Interface
interface IFile
{
    void ReadFile();
    void WriteFile(string text);
}

You cannot apply access modifiers to interface members. All the members are public by default. If you use an access modifier in an interface, then the C# compiler will give a compile-time error "The modifier 'public/private/protected' is not valid for this item.". (Visual Studio will show an error immediately without compilation.)

Example: Invalid Interface with Access Modifiers
interface IFile
{
    protected void ReadFile(); //compile-time error
    private void WriteFile(string text);//compile-time error
}

An interface can only contain declarations but not implementations. The following will give a compile-time error.

Example: Invalid Interface with Implementation
interface IFile
{
    void ReadFile();
    void WriteFile(string text){
        Console.Write(text);  //error: cannot implement method
    }
}

Implementing an Interface

A class or a Struct can implement one or more interfaces using colon (:).

Syntax: <Class or Struct Name> : <Interface Name>

For example, the following class implements the IFile interface implicitly.

Example: Interface Implementation
interface IFile
{
    void ReadFile();
    void WriteFile(string text);
}

class FileInfo : IFile
{
    public void ReadFile()
    {
        Console.WriteLine("Reading File");
    }

    public void WriteFile(string text)
    {
        Console.WriteLine("Writing to file");
    }
}

In the above example, the FileInfo class implements the IFile interface. It defines all the members of the IFile interface with public access modifier. The FileInfo class can also contain members other than interface members.

Note:
Interface members must be implemented with the public modifier; otherwise, the compiler will give compile-time errors.

You can create an object of the class and assign it to a variable of an interface type, as shown below.

Example: Interface Implementation
public class Program
{
    public static void Main()
    {
        IFile file1 = new FileInfo();
        FileInfo file2 = new FileInfo();
		
        file1.ReadFile(); 
        file1.WriteFile("content"); 

        file2.ReadFile(); 
        file2.WriteFile("content"); 
    }
}

Above, we created objects of the FileInfo class and assign it to IFile type variable and FileInfo type variable. When interface implemented implicitly, you can access IFile members with the IFile type variables as well as FileInfo type variable.

Explicit Implementation

An interface can be implemented explicitly using <InterfaceName>.<MemberName>. Explicit implementation is useful when class is implementing multiple interfaces; thereby, it is more readable and eliminates the confusion. It is also useful if interfaces have the same method name coincidently.

Note:
Do not use public modifier with an explicit implementation. It will give a compile-time error.
Example: Explicit Implementation
interface IFile
{
    void ReadFile();
    void WriteFile(string text);
}
    
class FileInfo : IFile
{
    void IFile.ReadFile()
    {
        Console.WriteLine("Reading File");
    }

    void IFile.WriteFile(string text)
    {
        Console.WriteLine("Writing to file");
    }
}

When you implement an interface explicitly, you can access interface members only through the instance of an interface type.

Example: Explicit Implementation
interface IFile
{
    void ReadFile();
    void WriteFile(string text);
}

class FileInfo : IFile
{
    void IFile.ReadFile()
    {
        Console.WriteLine("Reading File");
    }

    void IFile.WriteFile(string text)
    {
        Console.WriteLine("Writing to file");
    }

    public void Search(string text)
    {
        Console.WriteLine("Searching in file");
    }
}

public class Program
{
    public static void Main()
    {
        IFile file1 = new FileInfo();
        FileInfo file2 = new FileInfo();
		
        file1.ReadFile(); 
        file1.WriteFile("content"); 
        //file1.Search("text to be searched")//compile-time error 
        
        file2.Search("text to be searched");
        //file2.ReadFile(); //compile-time error 
        //file2.WriteFile("content"); //compile-time error 
    }
}

In the above example, file1 object can only access members of IFile, and file2 can only access members of FileInfo class. This is the limitation of explicit implementation.

Implementing Multiple Interfaces

A class or struct can implement multiple interfaces. It must provide the implementation of all the members of all interfaces.

Example: Implement Multiple Interfaces
interface IFile
{
    void ReadFile();
}

interface IBinaryFile
{
    void OpenBinaryFile();
    void ReadFile();
}

class FileInfo : IFile, IBinaryFile
{
    void IFile.ReadFile()
    {
        Console.WriteLine("Reading Text File");
    }

    void IBinaryFile.OpenBinaryFile()
    {
        Console.WriteLine("Opening Binary File");
    }

    void IBinaryFile.ReadFile()
    {
        Console.WriteLine("Reading Binary File");
    }

    public void Search(string text)
    {
        Console.WriteLine("Searching in File");
    }
}

public class Program
{
    public static void Main()
    {
        IFile file1 = new FileInfo();
        IBinaryFile file2 = new FileInfo();
        FileInfo file3 = new FileInfo();
		
        file1.ReadFile(); 
        //file1.OpenBinaryFile(); //compile-time error 
        //file1.SearchFile("text to be searched"); //compile-time error 
        
        file2.OpenBinaryFile();
        file2.ReadFile();
        //file2.SearchFile("text to be searched"); //compile-time error 
    
        file3.Search("text to be searched");
        //file3.ReadFile(); //compile-time error 
        //file3.OpenBinaryFile(); //compile-time error 
    }
}

Above, the FileInfo implements two interfaces IFile and IBinaryFile explicitly. It is recommended to implement interfaces explicitly when implementing multiple interfaces to avoid confusion and more readability.

Points to Remember :
  1. Interface can contain declarations of method, properties, indexers, and events.
  2. Interface cannot include private, protected, or internal members. All the members are public by default.
  3. Interface cannot contain fields, and auto-implemented properties.
  4. A class or a struct can implement one or more interfaces implicitly or explicitly. Use public modifier when implementing interface implicitly, whereas don't use it in case of explicit implementation.
  5. Implement interface explicitly using InterfaceName.MemberName.
  6. An interface can inherit one or more interfaces.