Introduction
Implementing interfaces in C# is an everyday programmer task. Most interfaces are implemented
using implicit technique without any doubt. Explicit implementation not only is unfamiliar, but
is considered to be a some awkward solution to a very rare problem of implementing two interfaces with same members.
In this article I'm going to discuss, why, in my opinion, explicit interface implementation - EIMI
-
should be considered as the default implementation technique. And how using EIMIs
keeps interfaces and their implementations in a coherent state.
Basics
For the beginning, let's recall what implicit and explicit interface implementation means.
Given an interface
ILogger
interface ILogger
{
void Trace(string format, params object[] args);
}
We usually implement it in the following way
class Logger : ILogger
{
public void Trace(string format, params object[] args) { }
}
This is an
implicit interface implementation.
The meaning of implicit here is - the compiler decides on its own that a public
method Logger.Trace(...)
will implement the corresponding ILogger.Trace(...)
interface member.
Let's put the above explicitly
class Logger : ILogger
{
void ILogger.Trace(string format, params object[] args) { }
}
This is an explicit interface implementation - EIMI
What MSDN tells us about the need of EIMI
?
Given two interfaces with the same members
interface IControl
{
void Paint()
}
interface ISurface
{
void Paint()
}
We need a way to distinguish the same members if we are going to implement both of the interfaces
in a single class
public class SampleClass : IControl, ISurface
{
void IControl.Paint() { }
void ISurface.Paint() { }
}
The Key Difference of Explicit Implementation
Explicitly implemented methods and properties
are private and inaccessible even to the implementing class
interface ILogger
{
void Trace(string format, params object[] args);
}
class Logger : ILogger
{
void ILogger.Trace(string format, params object[] args) { }
}
void main()
{
Logger log = new Logger();
log.Trace(""); }
Explicitly implemented methods can be accessed through interface only
void main()
{
ILogger log = new Logger();
log.Trace(""); }
The Glory of the Explicit Implementation
Refactoring
Let's implement some simple interface
ICalculator
.
Then, let's trace revisions of the interface as software is being developed.
interface ICalculator
{
void Solve(int startPoint);
}
class BasicCalculator : ICalculator
{
public void Solve(int startPoint) { }
}
Half a year later, obviously, the interface is enriched with an additional method
interface ICalculator
{
void Solve(int startPoint);
void Solve(int startPoint, int endPoint);
}
In this half a year other stuff was added to
BasicCalculator
, as well. And here is what
we might get after implementing the additional
Solve(int startPoint, int endPoint)
method
class BasicCalculator : ICalculator
{
public void Solve(int startPoint);
public void Solve(int startPoint, int endPoint);
}
In another half a year a major refactoring takes place, and the first
Solve(int startPoint)
method is removed from
the
ICalculator
interface.
interface ICalculator
{
void Solve(int startPoint, int endPoint);
}
Now, I will ask a question:
"Do you know many programmers there who bother to go over all implementations
and check if a particular change in interface leaves ghost public functions?"
class BasicCalculator : ICalculator
{
public void Solve(int startPoint);
public void Solve(int startPoint, int endPoint);
}
I doubt, that the answer is yes. So, the first version of
Solve(int startPoint)
method is left
inside all implementations of
ICalculator
interface forever.
With explicit implementation this will not going to happen !
interface ICalculator
{
void Solve(int startPoint, int endPoint);
}
class BasicCalculator : ICalculator
{
void ICalculator.Solve(int startPoint);
void ICalculator.Solve(int startPoint, int endPoint);
}
EIMI
forces the compiler to look all over the code and tells us that we no longer need to implement the Solve(int)
method.
In my opinion this "feature" is a killer and is a "must-to-use" in any large code base with many maintainers, especially if some of them are less experienced.
Decoupling From a Specific Implementation
There are other benefits of EIMIs
as well.
Consider the following code
interface ICalculator
{
void Solve(int startPoint);
}
class BasicCalculator : ICalculator
{
public void Solve(int startPoint);
public void UseSinToSolveTheProblemIfStartPointIsZero(int startPoint);
}
void main()
{
var calculator = new BasicCalculator();
calculator.Solve(123);
}
The var
keyword is very convenient and commonly used. As a result, in the example above the calculator
variable becomes an instance of
the BasicCalculator
class. This is obviously bad, since the code becomes coupled to a specific implementation of a general ICalculator
interface.
Later an inexperienced maintainer, will be tempted to use the calculator.UseSinToSolveTheProblemIfStartPointIsZero()
method.
With EIMI
the main()
above won't compile since Solve()
method is not accessible within the BasicCalculator
class.
We are forced to use the explicit ICalculator
declaration instead of just the var
keyword
void main()
{
ICalculator calculator = new BasicCalculator();
calculator.Solve(123);
}
Decoupling Public Interface From Implementation Details
And the last (for this article) benefit of
EIMI
.
Consider this code
interface ILogger
{
string Name { set; }
}
class Logger : ILogger
{
public string Name { get; set; }
private void GenerateAutoName()
{
Name = "MySophisticatedLogger";
}
}
Here a public property Name
is used to implement a private implementation detail inside the GenerateAutoName()
method.
Half a year later we will probably add a validation code for the Name
property
class Logger : ILogger
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (String.IsNullOrEmpty(value))
throw new BadArgumentException();
_name = value;
}
}
}
We no longer use the auto-implemented property feature of C#.
But what happens inside our private
GenerateAutoName()
method? The validation code is "injected" into the method as well.
private void GenerateAutoName()
{
Name = "MySophisticatedLogger";
}
Do we need the validation code of external data inside internal implementation?
I guess that the answer is no.
Avoiding use of own public methods in private implementation details is a good practice, IMHO.
interface ILogger
{
string Name { set; }
}
class Logger : ILogger
{
public string ILogger.Name { get; set; }
private void GenerateAutoName()
{
Name = "MySophisticatedLogger"; }
}
EIMI
forces us to add private field
_name
as soon as we are going to use it in a private implementation.
Conclusion
In my every day programming I implement interfaces. The explicit way of implementing is the default for me. I
use an implicit implementation only when I can not avoid it. Each time I'm refactoring, I see the actual benefits of EIMIs
.
Here is a nifty implementation of a template method design pattern using EIMI
interface ICalculator
{
void Solve();
}
abstract class CalculatorBase : ICalculator
{
protected abstract void CalculateSolution();
void ICalculator.Solve()
{
CalculateSolution();
}
}
I like the way the design is expressed here. The interface Solve()
method
delegates the implementation details to a protected virtual method overridden by descendants.
EIMI
keeps code cleaner and more maintainable.
Farther Reading
What is
EIMI
?
EIMIs
are good
EIMIs
are bad