How to Implement an Interface Without Making Members Public Using Explicit Interface Implementation
If you've ever implemented an interface, you've probably encountered the requirement that the member you are implementing must be public. For example, you cannot define a property in an interface and then make the setter internal as demonstrated by the following example:
If you attempt to compile the above code, you will receive an error stating that the class does not implement the interface member because it's not public.
There are use cases for implementing interfaces and make the members non-public. Building off the example above, suppose I want all entity objects to have a an Id property that anyone can get, but only internal classes can set.
I'll walk you through a series of code revisions and solve the problem using explicit interface implementation. Explicit implementation essentially hides the class member so that it can only be accessed through the interface. To use this feature, prefix the member you are implementing with the interface name, as demonstrated by the Id property below:
Now the Id property is no longer visible from the Customer class:
To access an explicitly implemented member, cast it to the interface that contains the member (IEntity in this example):
I don't want my consumers to have to cast entity objects to IEntity every time they need to access the Id property, so I'll add a read-only Id property on the class. At first it might feel strange having a member with the same name without using overloading, overriding or hiding (new keyword), but it actually is pretty logical. A class can implement many interfaces, and explicit implementation is the mechanism that handles name clashes when two interfaces define a member with the same name.
Although it may appear we have arrived at the solution, we aren't quite there yet. Explicit implementation hides the member, but it does not restrict access to it; consumers can still cast the object and access the member. To restrict access, we will split non-public members into an interface, and put an appropriate access modifier on the interface. This way consumers cannot cast to that interface and access explicitly implemented members, because they can't access the interface. Let's revisit the example and make some changes. First, the public interface should only contain that which is public, so we'll remove the setter.
Now we'll move the Id property setter into a new interface that is only visible internally:
Next, we'll update the Customer entity to explicitly implement the IEntityIdSetter interface:
For illustrative purposes and to demonstrate why we used interfaces in the first place, we'll also implement an Employee class that implements the same interfaces:
Now any class within the assembly can set the Id property and the interface enables us to operate on different types yet treat them as one. In this example, we don't care if it's an Employee or a Customer, just that it supports setting the Id:
If any class outside the assembly attempts to set the property, the compiler will display an error that the property is read only:
Summary
Sometimes you need to implement an interface and don't want a member to be public. To achieve this, perform the following steps:
- Separate non-public members into an interface
- Set the interface access modifier to internal (you can't use private, protected or protected internal)
- Explicitly implement the non-public interface member(s)
References