ぼやきごと/2015-02-13/C++CLI:同名のプロパティやメソッドを持つインタフェースの明示的実装 の変更点


#blog2navi()
*C++CLI:同名のプロパティやメソッドを持つインタフェースの明示的実装 [#j4e559be]

:ネタ元|[[c++ cli - C++/CLI: Implementing IList and IList<T> (explicit implementation of a default indexer) - Stack Overflow>http://stackoverflow.com/questions/689704/c-cli-implementing-ilist-and-ilistt-explicit-implementation-of-a-default-i]]

例えば独自のリストクラスを作る際に @code{IList<T>}; インタフェースと @code{IList}; インタフェースを両方実装したい場合があります。~
実装方法は色々あると思いますが、 @code{IList<T>}; を普通に実装し、 @code{IList}; は明示的実装をする方法が割と一般的かと思います。

C#で書くと次のような感じです。(一部抜粋)

#code(csharp){{
using System;
using System.Collections;
using System.Collections.Generic;

namespace Sample
{
    public class MyList<T> : IList<T>, IList
    {
        // IList<T> インデクサの実装
        public T this[int index]
        {
            get { /* 実装略 */ }
            set { /* 実装略 */ }
        }

        // IEnumerable<T>.GetEnumerator メソッドの実装
        public IEnumerator<T> GetEnumerator() { /* 実装略 */ }

        // IList インデクサの明示的実装
        object IList.this[int index]
        {
            get { return this[index]; }
            set { this[index] = (T)value; }
        }

        // IEnumerable.GetEnumerator メソッドの明示的実装
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        // …以下略…
    }
}
}}

上記のコードと同等の実装をC++/CLIで行おうとすると次のようになります。

#code(cppcli){{
using namespace System;
using namespace System::Collections;

namespace Sample
{
    generic<class T>
    public ref class MyList : Generic::IList<T>, IList
    {
    public:
        // IList<T> インデクサの実装
        property T default[int]
        {
            virtual T get(int index) sealed { /* 実装略 */ }
            virtual void set(int index, T value) sealed { /* 実装略 */ }
        }

        // IEnumerable<T>.GetEnumerator メソッドの実装
        virtual Generic::IEnumerator<T>^ GetEnumerator() sealed { /* 実装略 */ }

    protected:
        // IList インデクサの明示的実装
        property Object^ default[int]
        {
            virtual Object^ get(int index) sealed = IList::default::get
            {
                return this[index];
            }
            virtual void set(int index, Object^ value) sealed = IList::default::set
            {
                this[index] = safe_cast<T>(value);
            }
        }

        // IEnumerable.GetEnumerator メソッドの明示的実装
        virtual IEnumerator^ GetEnumerator() sealed = IEnumerable::GetEnumerator
        {
            return this->GetEnumerator();
        }

        // …以下略…
    };
}
}}

しかしこのコードはコンパイルエラーになります。

#pre{{
error C3296: 'Sample::MyList<T>::default' : この名前のプロパティが既に存在します
error C2556: 'System::Object ^Sample::MyList<T>::default::get(int)' :
             オーバーロード関数は、'T Sample::MyList<T>::default::get(int)' と戻り値の型のみが異なります。
error C2373: 'Sample::MyList<T>::default::get' : 再定義されています。異なる型修飾子です。
}}

C++/CLIでは、たとえインタフェースの明示的実装であっても、戻り値の型のみが異なるようなオーバロードはできません。

ではC++/CLIで同名のプロパティやメソッドを持つインタフェースを同時に実装することはできないのかというとそんなことはありません。~
実はC++/CLIでインタフェースの明示的実装を行うプロパティやメソッドの名前は、''元の名前と同じである必要はない''のです。

つまり、上述のコードを例えば次のように書き換えることができます。(変更部分のみ抜粋)

#code(cppcli,20-){{
    protected:
        // IList インデクサの明示的実装
        property Object^ default_[int]
        {
            virtual Object^ get(int index) sealed = IList::default::get
            {
                return this[index];
            }
            virtual void set(int index, Object^ value) sealed = IList::default::set
            {
                this[index] = safe_cast<T>(value);
            }
        }

        // IEnumerable.GetEnumerator メソッドの明示的実装
        virtual IEnumerator^ GetEnumerator_() sealed = IEnumerable::GetEnumerator
        {
            return this->GetEnumerator();
        }
}}

この例では元の名前の末尾にアンダースコアを付けていますが、名前は何でも構いません。~
C++/CLIでは名前付きインデクサを定義できるため、インデクサの別名による明示的実装も問題なくできます。

C++/CLIは意外と色々できるのですが、その書き方を見つけるまでがちょっと大変ですね。

RIGHT:Category: &#x5b;[[C++>ぼやきごと/カテゴリ/C++]]&#x5d;&#x5b;[[Visual Studio>ぼやきごと/カテゴリ/Visual Studio]]&#x5d;&#x5b;[[プログラミング>ぼやきごと/カテゴリ/プログラミング]]&#x5d; - 2015-02-13 00:31:05
----
RIGHT:&blog2trackback();
#comment(above)
#blog2navi()