views:

370

answers:

3

The current guidlelines for explicit member implementation recommend:

  • Using 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.
  • Expose an alternative way to access any explicitly implemented members that subclasses are allowed to override.

A good example of this is when you want to implement the IXmlSerializable interface. The ReadXml and WriteXml methods are expected to be called by the XmlSerializer and are not typically called directly by developers.

When providing an alternative way to access explicitly members you wish to allow to be overridden, it seems to make sense to call the explicitly implemented member so as to avoid code duplication. Consider the following:

using System;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace Demo
{
    /// <summary>
    /// Demonstrates explicit implementation of the IXmlSerializable interface.
    /// </summary>
    [Serializable(), XmlRoot(ElementName = "foo")]
    public class Foo : IXmlSerializable
    {
        //============================================================
        //  IXmlSerializable Implementation
        //============================================================
        #region GetSchema()
        /// <summary>
        /// Returns an <see cref="XmlSchema"/> that describes the XML representation of the object.
        /// </summary>
        /// <returns>
        /// An <see cref="XmlSchema"/> that describes the XML representation of the object that is 
        /// produced by the <see cref="IXmlSerializable.WriteXml(XmlWriter)"/> method and consumed by the <see cref="IXmlSerializable.ReadXml(XmlReader)"/> method.
        /// </returns>
        /// <remarks>This method is reserved and should not be used.</remarks>
        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }
        #endregion

        #region ReadXml(XmlReader reader)
        /// <summary>
        /// Generates an object from its XML representation.
        /// </summary>
        /// <param name="reader">The <see cref="XmlReader"/> stream from which the object is deserialized.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="reader"/> is a <b>null</b> reference (Nothing in Visual Basic).</exception>
        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            // Class state values read from supplied XmlReader
        }
        #endregion

        #region WriteXml(XmlWriter writer)
        /// <summary>
        /// Converts an object into its XML representation.
        /// </summary>
        /// <param name="writer">The <see cref="XmlWriter"/> stream to which the object is serialized.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="writer"/> is a <b>null</b> reference (Nothing in Visual Basic).</exception>
        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            // Current class state values written using supplied XmlWriter
        }
        #endregion

        //============================================================
        //  Public Methods
        //============================================================
        #region WriteTo(XmlWriter writer)
        /// <summary>
        /// Saves the current <see cref="Foo"/> to the specified <see cref="XmlWriter"/>.
        /// </summary>
        /// <param name="writer">The <see cref="XmlWriter"/> stream to which the <see cref="Foo"/> is serialized.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="writer"/> is a <b>null</b> reference (Nothing in Visual Basic).</exception>
        public void WriteTo(XmlWriter writer)
        {
            writer.WriteStartElement("foo");

            ((IXmlSerializable)this).WriteXml(writer);

            writer.WriteEndElement();
        }
        #endregion
    }
}

My question is in regards to how expensive the boxing of the WriteXml method is in this implementation. Is ((IXmlSerializable)this).WriteXml(writer) going to significantly hinder performance?

+3  A: 

No, the cost of writing a bunch of data to an XmlWriter is going to dwarf the boxing cost.

Boxing consists of:

  1. Allocating a slice of memory from the GC
  2. Initializing its header with the correct type information
  3. Copying the valuetype data into the heap memory

As such, it's about the same as an object construction. If even a single piece of data that you're writing to the XmlWriter isn't already a string, you'll have to pay this cost anyway, to construct the string to write.

Barry Kelly
+4  A: 

There's no boxing taking place in your example... it's just a cast, and it's resolvable at compile time, so it should not have any impact on performance at all.

Edit: Looking at it with ILDASM, the interface cast will give you a virtual method call versus a regular method call, but this is negligible (there is still no boxing involved).

Edit 2: If you use a struct instead of a class, THEN you will get a box going through the interface, with much more of a performance penalty.

Eric Rosenberger
Boxing only affects structs, so of course that was the question; but boxing isn't much different than newing up strings. The worst that can happen is the struct is huge, and the costs of copying it in are prohibitive; but then you have another problem, namely that copying the struct is expensive.
Barry Kelly
+1  A: 

Why not just have them both call a private method that performs the function of the explicitly implemented interface?

public void IXmlSerializable.WriteXml( XmlWriter writer )
{
    InternalWriteXml( writer );
}

public void WriteTo(XmlWriter writer)
{
    writer.WriteStartElement("foo");

    InternalWriteXml(writer);

    writer.WriteEndElement();
}

private void InternalWriteXml( XmlWriter writer )
{
    ...
}
tvanfosson