CS0~400

C# コンパイラエラー CS0021 の原因と対策について解説

CS0021 エラーは、C# のコンパイラがインデックス演算子 [] を、サポートしていない型に適用した場合に発生します。

特に C++ と連携する際、クラスに正しい既定メンバー属性 DefaultMember が設定されていないとエラーとなることがあります。

エラー発生箇所を点検し、適切なインデックス利用方法を検討してください。

CS0021 エラー発生の原因

C# コンパイラが CS0021 エラーを出す原因の一つに、インデックス演算子 [] をサポートしていない型へのアクセスが挙げられます。

インデクサーは、配列やリスト、または独自に定義されたプロパティなどで利用可能ですが、対象外の型に対してこの演算子を使用するとコンパイラがエラーとして検出します。

不正なインデクサー利用

不正なインデクサー利用は、プログラムの実装で索引アクセスが意図されたメンバーではなく、誤った型に対して [] を用いている場合に発生します。

たとえば、クラスや構造体にインデクサーが定義されていないにもかかわらず、インデックスアクセスを試みると、コンパイラがどのメンバーにアクセスすべきか判断できず、エラー CS0021 が出ることになります。

対象外のデータ型へのインデックス演算子適用

対象外のデータ型へのインデックス演算子の適用では、以下のようなコード例が挙げられます。

この例では、配列やリストではない型に対し、[] 演算子が使用されています。

// サンプルコード: 非対応型に対して [] を使用
public class SampleClass
{
    // インデクサーが定義されていないクラス
}
public class Program
{
    public static void Main()
    {
        SampleClass sample = new SampleClass();
        // 以下の行は SampleClass にインデクサーが存在しないためエラーとなる
        // int result = sample[0]; // CS0021 エラーの発生例
        // 適切な型(例: 配列)を使った場合
        int[] numbers = { 1, 2, 3 };
        int validResult = numbers[0];
        System.Console.WriteLine(validResult);
    }
}

上記の例では、SampleClass に対してインデックスアクセス sample[0] を行うとエラーが発生します。

インデクサーを利用する場合は、対象となる型に適切なプロパティやメソッドが定義されているか確認してください。

C++ アセンブリとの連携不備

C++ で作成されたアセンブリ(DLL)を C# から利用する場合、デフォルトでインデクサーとして認識されるメンバーを C# コンパイラが見つけられないケースがあります。

特に、C++ 側で DefaultMember 属性が設定されていなかったり、コメントアウトされている場合、C# でのインデクサー利用時に CS0021 エラーが発生するケースが報告されています。

DefaultMember 属性未設定の問題

C++ のコード内で DefaultMember 属性を正しく設定していない場合、C# コンパイラはどのプロパティをデフォルトのインデクサーとして扱えばよいか判断できません。

これにより、C# 側でインデックスを使用したアクセスを試みると、

Error: 角かっこ [] 付きインデックスを ‘type’型の式に適用することはできません

といったエラーが発生します。

以下は問題が発生する可能性のある C++ コードの一例です。

// CPP0021.cpp
// コンパイル: /clr /LD
using namespace System::Reflection;
// DefaultMember 属性がコメントアウトされており、以下のコードはエラーを発生させる
// [DefaultMember("myItem")]
public ref class MyClassMC
{
public:
    property int myItem[int]
    {
        int get(int i) { return 5; } // インデックスアクセス時に常に 5 を返す
        void set(int i, int value) { /* 値の設定処理 */ }
    }
};

属性コメントアウトによる影響

C++ コードにおいて、既に DefaultMember 属性が記述されていたとしても、コメントアウトしてしまうと C# コンパイラはデフォルトのインデクサーを特定できなくなります。

このため、同じ C++ アセンブリを参照する C# のコードでインデックスアクセスを試みると CS0021 エラーが発生します。

以下は、属性がコメントアウトされているためにエラーが発生する例です。

// 属性コメントアウト版の C++ コードサンプル
// [DefaultMember("myItem")] // 属性がコメントアウトされているため、デフォルトのインデクサーとして認識されない
public ref class MyClassMC
{
public:
    property int myItem[int]
    {
        int get(int i) { return 5; }
        void set(int i, int value) { /* 値の設定処理 */ }
    }
};

このような場合、C# コード側で以下のようにインデックスアクセスを行うとエラーとなります。

// CS0021.cs
// C++ DLL を参照している C# コード例
public class Program
{
    public static void Main()
    {
        MyClassMC mcInstance = new MyClassMC();
        // 以下の行で CS0021 エラーが発生
        int indexValue = mcInstance[1];
        System.Console.WriteLine(indexValue);
    }
}

これらの例から、C++ 側で属性の設定が正しく行われていないことが、インデクサー利用時に CS0021 エラーを引き起こす主要な要因であることがわかります。

CS0021 エラーの対策方法

C++ と C# の連携において CS0021 エラーを回避するための対策として、C++ 側で DefaultMember 属性を正しく設定する方法や、C# 側で利用する型の定義を見直す方法があります。

以下では、それぞれの対策方法について具体的な手順を紹介します。

DefaultMember 属性の正しい設定方法

C++ アセンブリ側で DefaultMember 属性を正しく設定することにより、C# コンパイラがデフォルトのインデクサーとして認識できるようになります。

これにより、インデックスアクセス [] が正常に機能するようになります。

C++ クラスへの属性追加手順

C++ コード内で DefaultMember 属性を追加する手順は以下の通りです。

  1. C++ のソースコード内で、属性を使用するために using namespace System::Reflection; を記述する。
  2. クラス定義の直前に、コメントアウトされずに [DefaultMember("プロパティ名")] を記述する。
  3. デフォルトで利用したいプロパティ名(例:"myItem")を正しく指定する。

以下は具体的な手順を示すコード例です。

// CPP0021_Fix.cpp
// コンパイル: /clr /LD
using namespace System::Reflection;
// DefaultMember 属性を有効にすることで、C# からのアクセスが可能になる
[DefaultMember("myItem")]
public ref class MyClassMC
{
public:
    property int myItem[int]
    {
        int get(int i) { return 5; } // インデックス i に対して 5 を返す
        void set(int i, int value) { /* 値の設定処理 */ }
    }
};

この修正により、C# 側から MyClassMCオブジェクトに対して [] を用いたアクセスが問題なく行えるようになります。

DefaultMember 属性の具体例

改めて、実際の利用シーンを想定した具体例をご紹介します。

下記の C++ コードは、DefaultMember 属性が正しく設定された例です。

// 正常な DefaultMember 設定付きの C++ コード例
// コンパイル: /clr /LD
using namespace System::Reflection;
[DefaultMember("myItem")]
public ref class MyClassMC
{
public:
    property int myItem[int]
    {
        int get(int i) { return 5; }  // 任意のインデックスに対して 5 を返すシンプルな実装
        void set(int i, int value) { /* 必要に応じた値の設定処理 */ }
    }
};

上記コードを用いれば、C# 側では以下のように問題なくインデックスアクセスが利用可能です。

// 修正後の C# コード例
public class Program
{
    public static void Main()
    {
        MyClassMC mcInstance = new MyClassMC();
        // DefaultMember 属性が有効なため、インデックスアクセスが正常に動作
        int resultValue = mcInstance[0];
        System.Console.WriteLine(resultValue); // 出力例: 5
    }
}

適用可能な型の選定とインデクサー利用

CS0021 エラーが発生する場面では、単に属性の設定だけで解決できる場合もありますが、そもそもインデクサーが利用可能な型を正しく選択することも重要です。

対象型がインデクサーをサポートしない場合は、型定義自体を再検討し、必要に応じてインデクサーを実装するか、アクセス方法を見直す必要があります。

型定義の見直しと調整方法

対象のクラスや構造体に対してインデクサーを利用する場合、まずその型がインデクサーをサポートしているか確認してください。

もしサポートされていない場合は、次のように独自のインデクサーを実装することで、意図した動作を実現できます。

// カスタムクラスにインデクサーを実装した例
public class IndexableClass
{
    private int[] values = new int[] { 10, 20, 30 };
    // インデクサーの実装。これにより ``[]`` 演算子を利用可能
    public int this[int index]
    {
        get
        {
            // インデックスが範囲内かどうかの確認も可能
            return values[index];
        }
        set
        {
            values[index] = value;
        }
    }
}
public class Program
{
    public static void Main()
    {
        IndexableClass indexable = new IndexableClass();
        // インデクサーが正しく実装されているため、正常に値が取得できる
        int valueAtIndex = indexable[1];
        System.Console.WriteLine(valueAtIndex); // 出力例: 20
    }
}

この例では、クラス IndexableClass に対してインデクサーを実装しており、[] を用いたアクセスが正しく動作します。

また、もしライブラリや外部アセンブリを利用される場合は、該当の型がインデクサーをサポートしているか、また必要な属性が設定されているかを事前に確認することが推奨されます。

まとめ

この記事では、C#コンパイラエラーCS0021の発生原因と対策方法について解説しています。

対象外の型に対してインデクサーを使用した際に発生するエラーや、C++アセンブリからC#への連携時にDefaultMember属性が未設定またはコメントアウトされることによる問題が分かります。

また、正しいDefaultMember属性の設定手順と、インデクサーを利用可能な型定義の見直し方法を学ぶことができます。

関連記事

Back to top button
目次へ