Freigeben über


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:

image

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.

image

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:

image

Now the Id property is no longer visible from the Customer class: 

image

To access an explicitly implemented member, cast it to the interface that contains the member (IEntity in this example):

image

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.

image 

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.

image

Now we'll move the Id property setter into a new interface that is only visible internally:

image

Next, we'll update the Customer entity to explicitly implement the IEntityIdSetter interface:

image

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:

image

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:

image

If any class outside the assembly attempts to set the property, the compiler will display an error that the property is read only:

image

 

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

Comments

  • Anonymous
    June 13, 2008
    Very nicely done.  Seems like a useful technique.

  • Anonymous
    June 14, 2008
    The comment has been removed

  • Anonymous
    June 15, 2008
    Anders: Your sample is practically identical to the first code in the article, except the setter is removed from the interface.  I think the author's technique is important because it shows that the setter can still be included in an interface, so you can use that style of programming, without exposing your setter to the outside or forcing internal code to reference the concrete class. Also, I do agree that auto-properties are the way to go for this scenario, but that really wasn't the point of the article - and the code can be more easily followed by people who haven't been exposed to the 3.x compilers.

  • Anonymous
    June 15, 2008
    Anders, there's a subtle but important difference in your solution and the author's one. Using your code it's impossible to SET Id through interface. Suppose you also have Employee class that implements IEntity. Using your solution one can't write the NullId function presented in the article.

  • Anonymous
    July 02, 2014
    That's the best solution to the problem I've found so far!