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 instance fields.

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

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

The above declares an interface named IFile. (It is recommended to start an interface name with the letter "I" at the beginning of an interface so that it is easy to know that this is an interface and not a class.) The IFile interface contains two methods, ReadFile() and WriteFile(string).

Note:
  • An interface can contain declarations of methods, properties, indexers, and events.
  • Default interface methods with implementation body are supported from C# 8.0.
  • An interface cannot contain constructors and fields.
  • Interface members are by default abstract and public.
  • You cannot apply access modifiers to interface members. Although, C# 8.0 onwards, you may use private, protected, internal, public, virtual, abstract, sealed, static, extern, and partial modifiers on certain conditions.

Implementing an Interface

A class or a Struct can implement one or more interfaces using colon :. On impmenting an interface, you must override all the members of an interface.

Syntax:
class ClassName : InterfaceName
{

}

For example, the following FileInfo class implements the IFile interface, so it should override all the members of IFile.

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 overrides 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.

Default Interface Methods

Till now, we learned that interface can contain method declarations only. C# 8.0 added support for virtual extension methods in interface with concrete implementations.

The virtual interface methods are also called default interface methods that do not need to be implemented in a class or struct.

Example: Default Interface Methods
interface IFile
{
    void ReadFile();
    void WriteFile(string text);

    void DisplayName()
    {
        Console.WriteLine("IFile");
    }
}

In the above IFile interface, the DisplayName() is the default method. The implementation will remain same for all the classes that implements the IFile interface. Note that a class does not inherit default methods from its interfaces; so, you cannot access it using the class instance.

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

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

public class Program
{
    public static void Main()
    {
        IFile file1 = new FileInfo();
        file1.ReadFile(); 
        file1.WriteFile("content"); 
        file1.DisplayName();

        FileInfo file2 = new FileInfo();
        //file2.DisplayName(); //compile-time error 
    }
}

Learn more on Default interface methods.

Modifiers in Interfaces

C# 8.0 allows private, protected, internal, public, virtual, abstract, sealed, static, extern, and partial modifiers in an interface.

  • The default access level for all interface members is public.
  • An interface member whose declaration includes a body is a virtual member unless the sealed or private modifier is used.
  • A private or sealed function member of an interface must have implementation body.
  • Interfaces may declare static members which can be accessed by interface name.

Learn more about modifiers in interfaces.

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.