Design Guideline Update: Explicit Member Implementation


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

style="mso-bookmark: OLE_LINK4">
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. style="mso-spacerun: yes">  For example consider the following
definition:


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

}


This code calls the IConvertable members:


style="MARGIN: 6pt 0in 6pt 0.25in; mso-add-space: auto; tab-stops: 315.0pt"> face="Courier New">int i = 42;
i.ToString(); style="mso-spacerun: yes">  // works
i.ToInt32(); style="mso-spacerun: yes">   // does not
compile
((IConvertible) i).ToInt32(); 
// works


style="MARGIN: 0in 0in 0pt; tab-stops: .5in; mso-list: none"> face=Verdana size=2>


style="MARGIN: 0in 0in 0pt 0.25in; TEXT-INDENT: -0.25in"> style="mso-fareast-font-family: Verdana; mso-bidi-font-family: Verdana"> style="mso-list: Ignore"> style="FONT: 7pt 'Times New Roman'">           
Do not use explicit
members as a security boundary. They can be called by any client who cast an
instance to the interface.


style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"> style="mso-list: Ignore">*<br src="file:///D:\DOCUME~1\brada\LOCALS~1\Temp\msohtml1\06\clip_image003.gif"
width=12>
style="FONT: 7pt 'Times New Roman'">     
Do use explicit
members to hide implementation details


style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"> style="mso-list: Ignore">*<br src="file:///D:\DOCUME~1\brada\LOCALS~1\Temp\msohtml1\06\clip_image003.gif"
width=12>
style="FONT: 7pt 'Times New Roman'">     
Do use explicit members to
approximate private interface implementations. style="mso-spacerun: yes">  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. style="mso-spacerun: yes">  IConvertable has 19 conversion methods
that would pollute the public interface of these types. style="mso-spacerun: yes">  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.


style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"> style="mso-list: Ignore">*<br src="file:///D:\DOCUME~1\brada\LOCALS~1\Temp\msohtml1\06\clip_image003.gif"
width=12>
style="FONT: 7pt 'Times New Roman'">     
Do expose an
alternative way to access any explicitly implemented members that subclasses are
allowed to override.
href="#_ftn1" name=_ftnref1> style="mso-special-character: footnote"> style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana; mso-fareast-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-ansi-language: EN-US; mso-bidi-language: AR-SA; mso-fareast-language: EN-US">[1] size=2>  Use the
same method name unless a conflict would arise.


The following example shows a type, class=embeddedcode> color=#a52a2a>ViolatingBase, that violates the rule and a
type, face="Courier New" color=#a52a2a>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();
        }
    }
}



Comments (11)

  1. Frank Hileman says:

    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.

  2. Kevin Dente says:

    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.

  3. Brad Abrams says:

    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.

  4. Frank Hileman says:

    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.

  5. Dan Green says:

    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. 🙂

  6. Kevin Dente says:

    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. 🙂

  7. Kevin Dente says:

    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.

  8. Ken Brubaker says:

    For my own reference, I thought I’d compile a quick list of design guidelines added by Brad Abrams, et al.

Skip to main content