インターフェイスを明示的に実装する場合のボクシングの費用
-
22-07-2019 - |
質問
明示的なメンバー実装の現在のガイドラインでは、以下を推奨しています。
- 明示的なメンバーを使用して、プライベートインターフェイスの実装を概算します。 インフラストラクチャの理由だけのためにインターフェースを実装する必要があり、開発者がこのタイプからそのインターフェースのメソッドを直接呼び出すことを決して期待しない場合、パブリックビューから明示的に「隠す」ためにメンバーを実装します。
- サブクラスがオーバーライドを許可されている明示的に実装されたメンバーにアクセスする代替方法を公開します。
この良い例は、 IXmlSerializable インターフェース。 ReadXml および WriteXml メソッドはXmlSerializerによって呼び出されることが期待されており、通常、開発者によって直接呼び出されることはありません。
オーバーライドを許可するメンバーを明示的にアクセスする別の方法を提供する場合、コードの重複を避けるために、明示的に実装されたメンバーを呼び出すことは理にかなっているようです。以下を考慮してください。
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
}
}
私の質問は、この実装における WriteXml メソッドのボクシングがどれほど高価かということです。 ((IXmlSerializable)this).WriteXml(writer)はパフォーマンスを著しく低下させますか?
解決
例ではボクシングは行われていません...これは単なるキャストであり、コンパイル時に解決可能であるため、パフォーマンスにはまったく影響を与えません。
編集: ILDASMで見ると、インターフェイスキャストは仮想メソッド呼び出しと通常のメソッド呼び出しを提供しますが、これはごくわずかです(ボクシングは関係ありません)。
編集2:クラスの代わりに構造体を使用すると、インターフェイスを通過するボックスが表示されますが、パフォーマンスが大幅に低下します。
他のヒント
いいえ、大量のデータをXmlWriterに書き込むコストは、ボクシングのコストを大幅に削減します。
ボクシングの構成:
- GCからメモリのスライスを割り当てる
- 正しいタイプ情報でヘッダーを初期化する
- 値型データをヒープメモリにコピーする
そのため、オブジェクトの構築とほぼ同じです。 XmlWriterに書き込むデータが1つでも文字列でない場合でも、書き込む文字列を作成するために、とにかくこのコストを支払う必要があります。
なぜ両方とも明示的に実装されたインターフェースの機能を実行するプライベートメソッドを呼び出すだけではないのですか?
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 )
{
...
}