CS0~400

C# CS0121 エラーについて解説 ~呼び出しのあいまいさの原因と対策

CS0121 エラーは、C# においてメソッドやプロパティの呼び出しが複数の候補に該当し、どれを利用すべきかコンパイラが判断できない場合に発生します。

呼び出し対象があいまいなため、明示的なキャストや引数の型指定で、一意に決定するよう修正すると解決できます。

エラー発生の背景

メソッドとプロパティの呼び出し構成

オーバーロード定義の重複

メソッドやプロパティで同一パラメーター型のオーバーロード定義が複数存在すると、コンパイラはどの定義を呼び出せばよいか判断できず、エラー CS0121 が発生します。

たとえば、次のような状況では、同じ引数型で異なるメソッド定義が存在するため、呼び出しの際にどちらを選択するかあいまいになります。

呼び出しのあいまい性が生じる例

呼び出し側で複数のメソッド候補が存在し、どちらも適用可能な場合、コンパイラは選択できなくなります。

以下のサンプルコードは、引数の型により異なるメソッドが存在するにもかかわらず、呼び出しがあいまいになってエラーが起こる例です。

using System;
namespace AmbiguousCallExample
{
    class Program
    {
        // 同じ数の引数でも型が異なる場合、曖昧になる可能性がある
        public static void Process(int value)
        {
            // 日本語:整数を処理する
            Console.WriteLine("整数: " + value);
        }
        public static void Process(double value)
        {
            // 日本語:実数を処理する
            Console.WriteLine("実数: " + value);
        }
        static void Main(string[] args)
        {
            // 日本語:引数 1 は整数でも実数でも解釈可能なため、あいまいな呼び出しとなる
            Process(1);
        }
    }
}
// コンパイル時にエラーが発生します(CS0121)

コンパイラの動作と制約

明示的キャストの必要性

コンパイラは、暗黙的な型変換が複数定義されている場合、どの変換を使用すべきか判断できなくなることがあります。

その場合、明示的にキャストを指定することで、どの型の引数を使用するか明確に示す必要があります。

たとえば、上記のコード例では、Process((double)1) のようにキャストを行うと、実行するメソッドが明示され、エラーを回避することができます。

CS0121 エラーの原因詳細

引数型の不一致と暗黙の変換

暗黙の型変換による影響

引数として渡される値が複数の型に暗黙的に変換可能な場合、コンパイラはどの変換が適切か決定できず、あいまいさが発生します。

たとえば、整数リテラルは整数型だけでなく、実数型へも暗黙的に変換されるため、両方のオーバーロードが候補になり得ます。

これにより、呼び出しがあいまいであると判断され、エラーが発生します。

明示的キャストで解消する事例

明示的キャストを使用することで、コンパイラに対して明確な型情報を提供でき、どのオーバーロードを呼び出すかを特定できます。

以下のサンプルコードは、明示的キャストを使用してあいまいさを解消する例です。

using System;
namespace ExplicitCastExample
{
    class Program
    {
        public static void Process(int value)
        {
            // 日本語:整数処理用メソッド
            Console.WriteLine("整数: " + value);
        }
        public static void Process(double value)
        {
            // 日本語:実数処理用メソッド
            Console.WriteLine("実数: " + value);
        }
        static void Main(string[] args)
        {
            // 日本語:明示的にキャストすることで、どちらのメソッドを呼ぶのか明確に指定する
            Process((double)1);
        }
    }
}
実数: 1

ユーザー定義変換演算子の競合

競合検出の仕組み

ユーザー定義変換演算子が複数宣言されている場合、暗黙的または明示的な変換においてどの変換演算子を使用するか判断できず、呼び出しがあいまいになることがあります。

コンパイラは、候補となる変換演算子の中で最適なものを選定しようと試みますが、どれも適切であれば明確な選択ができないため CS0121 エラーを出します。

この状況では、どの変換演算子が利用されるべきかコード上で明示するか、変換演算子の定義自体を見直す必要があります。

コード例による検証

エラー再現コードの事例

再現条件と主要ポイント

以下のサンプルコードでは、Processメソッドのオーバーロードとユーザー定義変換演算子の両方が存在する場合の呼び出しを示します。

このコードは、引数の型に対して複数の候補が存在することで、呼び出しがあいまいになりエラーが発生する状況を再現します。

using System;
namespace ReproductionExample
{
    class Convertible
    {
        public int Number { get; set; }
        // 日本語:整数から Convertible へのユーザー定義変換
        public static implicit operator Convertible(int n)
        {
            return new Convertible { Number = n };
        }
        // 日本語:実数から Convertible へのユーザー定義変換
        public static implicit operator Convertible(double n)
        {
            return new Convertible { Number = (int)n };
        }
    }
    class Program
    {
        public static void Process(Convertible value)
        {
            // 日本語:ユーザー定義型を処理するメソッド
            Console.WriteLine("変換された数値: " + value.Number);
        }
        static void Main(string[] args)
        {
            // 日本語:整数リテラルは Convertible への変換が2通りあるため、あいまいと判断されエラーとなる
            Process(5);
        }
    }
}
// コンパイル時にエラーが発生します(CS0121)

修正前後のコード比較

修正手法の具体例

次の例では、修正前のコードでは暗黙の変換が複数存在してあいまいさを生じていますが、修正後は明示的なキャストにより呼び出すべきオーバーロードを明確にしています。

<修正前>

using System;
namespace FixBeforeExample
{
    class Convertible
    {
        public int Number { get; set; }
        // 暗黙の変換が2通り定義されている
        public static implicit operator Convertible(int n)
        {
            return new Convertible { Number = n };
        }
        public static implicit operator Convertible(double n)
        {
            return new Convertible { Number = (int)n };
        }
    }
    class Program
    {
        public static void Process(Convertible value)
        {
            Console.WriteLine("変換された数値: " + value.Number);
        }
        static void Main(string[] args)
        {
            // あいまいな呼び出しによりエラーが発生
            Process(5);
        }
    }
}

<修正後>

using System;
namespace FixAfterExample
{
    class Convertible
    {
        public int Number { get; set; }
        public static implicit operator Convertible(int n)
        {
            return new Convertible { Number = n };
        }
        public static implicit operator Convertible(double n)
        {
            return new Convertible { Number = (int)n };
        }
    }
    class Program
    {
        public static void Process(Convertible value)
        {
            Console.WriteLine("変換された数値: " + value.Number);
        }
        static void Main(string[] args)
        {
            // 明示的なキャストにより、適切な変換を利用する
            Process((Convertible)5);
        }
    }
}
変換された数値: 5

エラー解決の実施手順

コード修正手法の整理

オーバーロード宣言の見直し

同一のパラメーター型による重複定義は、コードの保守性を低下させるため、オーバーロード宣言を整理して重複が無いようにする必要があります。

場合によっては、メソッド名を分ける、またはパラメーターの型や数を変更するなどして、呼び出しが一意に決定されるように修正することが有効です。

引数型の明確化アプローチ

呼び出し時のあいまいさを解消するために、引数の型を明確に定義する工夫が必要です。

たとえば、明示的なキャストを利用してどの型に変換するかを指定する、あるいはメソッドの引数として異なる型を受け入れる別のオーバーロードを実装する方法があります。

実行テストと検証手順

再現性確認の方法

修正後は、必ず実行テストを行い、明示的キャストやオーバーロード宣言の変更が効果を発揮しているか検証する必要があります。

再現性の確認には以下の手順を推奨します。

・修正前のコードでエラーが発生していた状況を再現する

・修正後のコードでコンパイルエラーが解消され、期待する出力が得られるか確認する

・異なる引数パターンでテストを行い、あいまいな呼び出しが再び発生しないことを確認する

以上の手順により、コードの変更がエラー解決に寄与しているか、また他の部分に影響がないかを検証できます。

まとめ

この記事では、C# の CS0121 エラーについて、メソッドやプロパティのオーバーロードの重複や呼出しのあいまい性が原因となる状況を解説しています。

暗黙の型変換やユーザー定義変換演算子の競合がエラー発生に影響する点を示し、明示的キャストの利用やオーバーロード宣言の見直しといった修正方法を具体的なコード例で説明しています。

これにより、エラー解決の手順や実行テストの方法が理解できる内容となっています。

関連記事

Back to top button
目次へ