Design Guideline Update: Explicit Member Implementation

Long overdue update the the guidelines on Explicit member implementation. As always, feedback welcome.


Explicit Member
  Implementation

Explicit member implementation allows an interface member to be implemented such that it is only available when cast to the interface type.  For example consider the following definition:

public struct Int32 : IConvertible, IComparable {
   public override string ToString () {..}
   int ICovertible.ToInt32 () {..}
   ...
}

This code calls the IConvertable members:

int i = 42;
i.ToString();  // works
i.ToInt32();   // does not compile
((IConvertible) i).ToInt32();  // works

            Do not use explicit members as a security boundary. They can be called by any client who cast an instance to the interface.

*      Do use explicit members to hide implementation details

*      Do use explicit members to approximate private interface implementations.  If you need to implement an interface for only infrastructure reasons and you NEVER expect developers to directly call methods on that interface from this type then implement the members explicitly to “hide” them from public view.
For example, all of the base datatypes must implement IConvertable for infrastructure reasons.  IConvertable has 19 conversion methods that would pollute the public interface of these types.  So it is appropriate to explicitly implement these members so they are available when cast to IConvertable but do not appear on the public interfaces.  This makes the development experience much cleaner.

*      Do expose an alternative way to access any explicitly implemented members that subclasses are allowed to override.[1]  Use the same method name unless a conflict would arise.

The following example shows a type, ViolatingBase, that violates the rule and a type, FixedBase, that shows a fix for the violation.

using System;
 
namespace DesignLibrary
{
    public interface ITest
    {
        void SomeMethod();
    } 
    public class ViolatingBase : ITest
    {
        void ITest.SomeMethod()
        {
            // ...
        }
    }
    public class FixedBase : ITest
    {
        void ITest.SomeMethod()
        {
            SomeMethod();
        }
 
        virtual protected void SomeMethod()
        {
            // ...
        }
    }
    public class Derived : FixedBase, ITest
    {
        override protected void SomeMethod()
        {
            // ...
            base.SomeMethod();

            // This would cause recursion and a stack
            // overflow.
            // ((ITest)this).SomeMethod();
        }
    }
}

[1] Fully Covered by ExplicitMethodImplementationsInUnsealedClassesHaveVisibleAlternates

Published 15 November 03 10:35 by BradA

Comments

# Frank Hileman said on November 15, 2003 1:05 PM:
I think the following rule should be qualified: "Do use explicit members to approximate private interface implementations." People should be warned of the performance costs of this technique when it is done on value types. Namely, a boxing operation each time one of the functions is called. Hiding all the conversion functions for primitives in IConvertable was a mistake! This causes a severe performance penalty every time one to uses them, because of the boxing operations. And why do you use the word "pollutes"? Conversion between primitive data types is a common operation. Why should these functions be hidden? I don't see it making the development experience "cleaner." This is a poor example for the guideline.
# Kevin Dente said on November 15, 2003 2:42 PM:
Is the fact that subclasses can't override a method of a privately implemented interface by design? Are there any plans for C#/Whidbey to allow this? It would be very useful at times, and make privately implemented interfaces more appealing.
# Brad Abrams said on November 15, 2003 4:06 PM:
Frank is right – there is a cost to doing explicit member implementation in terms of boxing. In the case of IConvertable on Int32 etc we wanted to direct people to use Convert.ToXxx methods which don’t have the boxing costs and have the additional feature that all the conversion methods are available on the same class providing one-stop-shopping. Kevin – Yup, this is by design… I have not heard the C# folks talk about any fixes for this.
# Frank Hileman said on November 15, 2003 10:33 PM:
When first looking at primitive conversion, a long time ago, I saw IConvertible, but then realized it would do boxing. So I looked up the Convert class, and read this in the class summary: "Many of the methods in this class convert a source type to a target type by invoking the corresponding IConvertible explicit interface implementation method on the source object." So I thought IConvertible must be the way to go, since Convert just adds overhead. As you can see, the docs did not direct me in the right direction. Would be nice to fix this doc problem. Looking at Convert in a dissassembler I can see it does the correct, efficient thing, at least with 1.1. It does not use IConvertible in most cases at all. So that is the way to go. A better example for explicit member implementation would be GetEnumerator, where the generic object returning version is hidden, in preference to the type specific version. This is very common.
# Dan Green said on November 16, 2003 8:08 PM:
Brad-- I'd be interested in a discussion of this recommendation with a direct focus on IDisposable, given its unique language support (in the way of the using statement). For example, System.IO.Stream (and therefore derivatives) do explicit implementation, as does System.Resources.ResourceReader. Yet, System.Resources.ResourceWriter does not hide IDisposable. I personally dislike explicit implementation of IDisposable as it complicates teaching and learning good resource management techniques in the current version of VB (I realise this particular issue goes away with Whidbey -- yay). People like consistency. They want an easy to follow process -- namely, (1) check if object implements IDisposable; (2) if yes, then call Dispose() when finished with it, etc. It's not fun trying to learn (or teach) non-deterministic finalisation and suffer a digression and the need to understand explicit member initialisation. Currently (2) is more complicated than it need be. If IDisposable objects are *always* to be dealt with by using statements (compiler writers take notice) then I'm happy for it to be *always* hidden. If not, then how about a recommendation not to hide it? Regardless of the decision made, I'd like to see a decree from above on this particular item. :)
# Kevin Dente said on November 16, 2003 11:15 PM:
Brad - bummer that private interface overriding isn't being looked at. I just ran into it again. I'm creating a collection using NameObjectCollectionBase as the base class, and since it overrides GetEnumerator privately, I can't provide my own enumerator class (and, incidentally, it enumerates keys rather than values - odd choice). CollectionBase seems to have the same problem. Ah, but then generics will resolve these issues anyway. Whidbey, take me away. :)
# Kevin Dente said on November 16, 2003 11:31 PM:
Brad - oops I just found out how to implement a custom enumerator on a CollectionBase-derived class. Not perfect, since it involves shadowing the original GetEnumerator, but it'll do for now, I guess.
# Ken Brubaker said on February 20, 2004 10:46 AM:
For my own reference, I thought I'd compile a quick list of design guidelines added by Brad Abrams, et al.
# Geek Noise said on July 17, 2004 3:59 PM:
# Brian Di Croce » Blog Archive » Explicit Iterface Members Implementation in C# said on November 13, 2007 11:51 PM:

PingBack from http://blog.briandicroce.com/2007/11/13/explicit-iterface-members-implementation-in-c/

New Comments to this post are disabled

Search

Go

This Blog

Syndication

Page view tracker