A bit about the interfaces in .Net (based on one interview)

    Last Monday, I was lucky enough to get an interview at Senior .Net Developer in one international company. During the interview, I was asked to take a test, where a number of questions were related to .Net. In particular, in one of the questions it was necessary to give an assessment (true / false) of a number of statements, among which there was also such:

    In .Net, any array of elements, for example int [], implements IList by default, which allows you to use it as a collection in the foreach statement.


    Quickly answering this question negatively and separately adding in the margins. that foreach requires an implementation not of IList, but IEnumerable, I went on to the next question. However, on the way home, I was tormented by the question: does the array still implement this interface or not?

    About IList, I vaguely remembered that this interface gave me an IEnumerable, an indexer, and a Count property containing the number of elements in the collection, as well as a couple more rarely used properties, such as IsFixedCollection (). The array has the Length property for its size, and Count in IEnumerable is an extension method from LINQ, which would not be possible if this method were implemented in the class. Thus, it turned out that the array could not implement the IList interface, however, some vague feeling haunted me. Therefore, in the evening after the interview, I decided to conduct a little research.


    Class System.Array


    Since Reflector.Net was not installed for me, I just wrote a short program in C # to find out what interfaces are implemented by an integer array.

    var v = new int[] { 1, 2, 3 };
    var t = v.GetType();
    var i = t.GetInterfaces();
    foreach(var tp in i)
         Console.WriteLine(tp.Name);
    


    Here is the complete list of interfaces received from the console window:

    ICloneable
    IList
    ICollection
    IEnumerable
    IStructuralComparable
    IStructuralEquatable
    IList`1
    ICollection`1
    IEnumerable`1
    IReadOnlyList`1
    IReadOnlyCollection`1
    


    Thus, an array in .Net still implements the IList interface and its generic version of IList <> .

    To get more complete information, I built a diagram of the System.Array class.



    My mistake immediately struck me: Count was not a property of IList, but ICollection, the previous interface in the inheritance chain. However, the array itself did not already have such a property as many other properties of the IList interface, although other properties of this interface, IsFixedSize and IsReadOnly were implemented. How is this even possible?

    Everything immediately falls into place when you remember that in C # you can implement interfaces not only
    implicitly, but also explicitly. I knew about this possibility from textbooks, which gave an example of such an implementation in the case. when the base class already contains a method with the same name as the interface method. I also saw such an opportunity in ReSharper. However, to date, I have not directly faced the need to explicitly implement interfaces in my own projects.

    Comparison of explicit and implicit implementation of interfaces


    Let's compare these two types of implementation of interfaces :.

    Criteria
    Implicit implementation
    Explicit implementation
    Basic syntax
    interface ITest
    {
        void DoTest();
    }
    public class ImplicitTest : ITest
    {
        public void DoTest()
        { }
    }
    

    interface ITest
    {
        void DoTest();
    }
    public class ExplicitTest : ITest
    {
        void ITest.DoTest()
        { }
    }
    


    Visibility
    Implicit implementation has always been public, so methods and properties can be accessed directly.
    var imp = new ImplicitTest();
    imp.DoTest();
    

    Explicit implementation is always closed (private).
    To gain access to the implementation, you must cast the class instance to the interface (upcast to interface).
    var exp = new ExplicitTest();
    ((ITest)exp).DoTest();
    

    Polymorphy
    Implicit interface implementation can be virtual, which allows you to rewrite this implementation in descendant classes.
    Explicit implementation is always static. It cannot be rewritten (override) or overlapped (new) in child classes. Note 1
    Abstract class and implementation
    Implicit implementation can be abstract and implemented only in the descendant class.
    An explicit implementation may not be abstract, but the class itself may have other abstract methods and be abstract itself. Note 2

    Notes:
    Note 1 - As mayorovp rightly observes in the comments, the implementation can be overridden by re-explicitly implementing the interface in the descendant class (see the first comment on the article).

    Note 2 - One of the blogs indicated that the class itself cannot be abstract. Perhaps this was true for some of the previous versions of the compiler, in my experiments I could easily implement the interface explicitly in an abstract class.

    Why do we need an explicit implementation of interfaces?


    An explicit interface implementation, according to MSDN , is necessary when several interfaces implemented by a class have a method with the same signature. This problem is generally known in the English-speaking world under the chilling name “deadly diamond of death” , which translates into Russian as the “diamond problem” . Here is an example of such a situation:

    /*   Listing 1   */
    interface IJogger
    {
      void Run();
    }
    interface ISkier
    {
      void Run();
    }
    public class Athlete: ISkier, IJogger
    {
    	public void Run() 
    	{
    	   Console.WriteLine("Am I an Athlete, Skier or Jogger?");
    	}
    }
    


    By the way, this example is correct code in C #, that is, it (correctly) compiles and runs, while the Run () method is both a method of the class itself and an implementation of as many as two interfaces. Thus, we can have one implementation for different interfaces and for the class itself. You can verify this with the following code:

    /*   Listing 2   */
    var sp = new Athlete();
    sp.Run();
    (sp as ISkier).Run();
    (sp as IJogger).Run();
    


    The result of this code will be “Am I an Athlete, Skier or Jogger?” , Displayed in the console three times.

    It is here that we can use an explicit implementation of the interface in order to separate all three cases:

    /*   Listing 3   */
    public class Sportsman
    {
    	public virtual void Run()
    	{
    		Console.WriteLine("I am a Sportsman");
    	}
    }
    public class Athlete: Sportsman, ISkier, IJogger
    {
    	public override void Run() 
    	{
    	   Console.WriteLine("I am an Athlete");
    	}
    	void ISkier.Run() 
    	{
    	   Console.WriteLine("I am a Skier");
    	}
    	void IJogger.Run() 
    	{
    	   Console.WriteLine("I am a Jogger");
    	}
    }
    


    In this case, when executing the code from Listing 2, we will see three lines in the console, “I am an Athlete” , “I am a Skier” and “I am a Jogger” .

    Pros and cons of various interface implementations


    Implementation Visibility and Custom Implementation

    As shown above, an implicit implementation is not syntactically different from the usual class method (if this method has already been defined in the ancestor class, then in this syntax the method will be hidden in the descendant and the code will be compiled without any problems c compiler warning about hiding the method.). Moreover, selective implementation of individual methods of one interface is possible both explicitly and implicitly:

    /* Listing 4 */
    public class Code
    {
      public void Run() 
      {
      	Console.WriteLine("I am a class method");
      }
    }
    interface ICommand
    {
      void Run();
      void Execute();
    }
    public class CodeCommand : Code, ICommand
    {
      // implicit interface method implementation
      //  => public implementation
      // implicit base class method hiding (warning here)
      public void Run() 
      {
    	base.Run();
      }
      // explicit interface method implementation
      //  => private implementation
      void ICommand.Execute()
      {}
    }
    


    This allows you to use implementations of individual interface methods as native class methods and they are available, for example, through IntelliSense, in contrast to the explicit implementation of methods that are private and visible only after casting to the corresponding interface.

    On the other hand, the possibility of private methods implementation allows you to hide a number of interface methods, while fully implementing it. Returning to our very first example with arrays in .Net, we can see that the array hides, for example, the implementation of the Count property of the ICollection interface, exposing this property under the name Length (this is probably an attempt to maintain compatibility with C ++ STL and Java). Thus, we can hide certain methods of the implemented interface and not hide (= make public) others.

    Here, however, such a problem arises that in many cases it is completely impossible to guess which interfaces are implemented by the class “implicitly”, since neither the methods nor the properties of these interfaces are visible in IntelliSense (the example with System.Array is also indicative here). The only way to detect such implementations is to use reflection, for example using the Object Browser in Visual Studio.

    Interface Refactoring

    Since the implicit (public) implementation of an interface does not differ from the implementation of a public class method, in case of refactoring the interface and removing some public method from it (for example, when combining the Run () and Execute () methods from the above ICommand interface into one Run ( )) in all implicit implementations there will remain an open access method, which is very likely to have to be supported even after refactoring, since this public method may already have different dependencies in other components of the system. As a result of this, the programming principle “against interfaces, not implementations” will be violated, since the dependencies will already be between specific (and in different classes, probably different) implementations of the former interface method.

    /* Listing 5 */
    interface IFingers
    {
      void Thumb();
      void IndexFinger();
      // an obsolete interface method
      // void MiddleFinger();  
    }
    public class HumanPalm : IFingers
    {
      public void Thumb() {}
      public void IndexFinger() {}
      // here is a "dangling" public method
      public void MiddleFinger() {}
    }
    public class AntropoidHand : IFingers
    {
       void IFingers.Thumb() {}
       void IFingers.IndexFinger() {}
       // here the compiler error
       void IFingers.MiddleFinger() {}
    }
    


    In the case of a private implementation of interfaces, all classes with an explicit implementation of a more non-existent method will simply cease to compile, however, after deleting the implementation that has become unnecessary (or refactoring it into a new method), we will not have an "extra" public method that is not bound to any interface. Of course, it may be necessary to refactor the dependencies on the interface itself, but at least there will be no violation of the principle of “program to interfaces, not implementations”.

    As for properties, implicitly implemented interface properties (properties) allow you to access them through accessor methods (getter and setter) both externally and directly from the class itself, which can lead to unnecessary effects (for example, unnecessary data validation during initialization properties).

    /* Listing 6 */
    interface IProperty
    {
      int Amount { get; set; }
    }
    public class ClassWithProperty : IProperty
    {
        // implicit implementation, public
    	public int Amount { get; set; }
    	public ClassWithProperty()
    	{
    	    // internal invocation of the public setter
    		Amount = 1000;
    	}
    }
    public class ClassWithExplicitProperty : IProperty
    {
          // explicit implementation, private
    	int IProperty.Amount { get; set; }
    	public ClassWithExplicitProperty()
    	{
    	    // internal invocation isn't possible
    	     // compiler error here
    	    Amount = 1000;
    	}
    }
    


    With the explicit implementation of interface properties, these properties remain private and for access you have to go the long way and declare an additional private field through which initialization takes place. As a result, this leads to cleaner code when property access methods are used only for external access.

    Using explicit typing of local variables and class fields

    In the case of the explicit implementation of interfaces, we have to explicitly indicate that we are working not with an instance of a class, but with an instance of an interface. Thus, for example, it becomes impossible to use type inference and declare local variables in C # using the service word var. Instead, we have to use an explicit declaration indicating the type of interface when declaring local variables, as well as in the signature of methods and in the fields of the class.

    Thus, on the one hand, we make the code somewhat less flexible (for example, ReSharper always suggests using a declaration with var if possible), but we avoid potential problems associated with binding to a specific implementation as the system grows and its size grows. code. This point may seem a lot of controversial, but in the case when several people are working on the project, and even in different parts of the world, using explicit typing can be very useful, as this increases the readability of the code and reduces the cost of maintaining it.

    Related Sources
    In preparing the article, information was used from a number of network sources, in particular from blogs ( [1] , [2] , [3] and [4] ), as well as from [5] and [6] questions from StackOverflow, a very interesting article on CodeProject and chapters 13.5 of Jeffrey Richter's " CLR via C # ".
    A small bonus: two questions for backfilling (for the inquisitive)
    These questions are not directly related to the topic of explicit implementation of interfaces, but it seems to me that they may be of interest to someone here:
    1. If you add one more line to Listing 2
    (sp as Sportsman).Run();
    

    What will be displayed in the console?

    2. How can I get the phrase “I am a Sportsman” in the first question to the console using the minimum change in Listing 3 (replacing one keyword with another) ?

    Also popular now: