Code Fix

中級

cannot be used as type parameter の原因と直し方

where T : IComparable<T>のような制約を満たさない型を、ジェネリックメソッドの型引数として渡そうとすると出るコンパイルエラーです。

エラーメッセージの読み方

Program.cs(7,34): error CS0311: The type 'Program.NotComparable' cannot be used as type parameter 'T' in the generic type or method 'Program.Max<T>(T, T)'. There is no implicit reference conversion from 'Program.NotComparable' to 'System.IComparable<Program.NotComparable>'.
Program.cs
ファイル名
7
行番号
34
列番号 — この位置でコンパイラが解析に失敗しました
CS0311
エラーコード — 検索するとMicrosoftの解説ページが見つかります
The type 'Program.NotComparable' cannot be used as type parameter 'T' in the generic type or method 'Program.Max<T>(T, T)'. There is no implicit reference conversion from 'Program.NotComparable' to 'System.IComparable<Program.NotComparable>'.
内容 — 期待していたもの、または受け付けられなかったもの

このエラーが出る典型パターン

パターン1

 1  class Program {
 2      static T Max<T>(T a, T b) where T : System.IComparable<T> {
 3          return a.CompareTo(b) > 0 ? a : b;
 4      }
 5      class NotComparable { }
 6      static void Main(string[] args) {
 7          System.Console.WriteLine(Max(new NotComparable(), new NotComparable()));
                                                             ^
 8      }
 9  }
Program.cs(7,34): error CS0311: The type 'Program.NotComparable' cannot be used as type parameter 'T' in the generic type or method 'Program.Max<T>(T, T)'. There is no implicit reference conversion from 'Program.NotComparable' to 'System.IComparable<Program.NotComparable>'.

MaxはIComparable<T>を実装した型しか受け取れません。独自クラスにその実装がなければ型引数として使えません。

直し方: new NotComparable(), new NotComparable() を 1, 2 にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  class Program {
 2      static T Clamp<T>(T value, T min, T max) where T : System.IComparable<T> {
 3          if (value.CompareTo(min) < 0) return min;
 4          if (value.CompareTo(max) > 0) return max;
 5          return value;
 6      }
 7      class Tag { }
 8      static void Main(string[] args) {
 9          System.Console.WriteLine(Clamp(new Tag(), new Tag(), new Tag()));
                                                          ^
10      }
11  }
Program.cs(9,34): error CS0311: The type 'Program.Tag' cannot be used as type parameter 'T' in the generic type or method 'Program.Clamp<T>(T, T, T)'. There is no implicit reference conversion from 'Program.Tag' to 'System.IComparable<Program.Tag>'.

ClampもIComparable<T>制約を課しています。比較できない型は制約違反としてコンパイル時に弾かれます。

直し方: new Tag(), new Tag(), new Tag() を 5, 1, 10 にします。

この問題を解いてみる →

パターン3

 1  class Program {
 2      static bool IsGreater<T>(T a, T b) where T : System.IComparable<T> {
 3          return a.CompareTo(b) > 0;
 4      }
 5      class Blob { }
 6      static void Main(string[] args) {
 7          System.Console.WriteLine(IsGreater(new Blob(), new Blob()));
                                                          ^
 8      }
 9  }
Program.cs(7,34): error CS0311: The type 'Program.Blob' cannot be used as type parameter 'T' in the generic type or method 'Program.IsGreater<T>(T, T)'. There is no implicit reference conversion from 'Program.Blob' to 'System.IComparable<Program.Blob>'.

IsGreaterも比較可能な型しか受け付けません。制約は実行前、コンパイル時にチェックされます。

直し方: new Blob(), new Blob() を 3, 1 にします。

この問題を解いてみる →

パターン4

 1  class Program {
 2      static T Max<T>(T a, T b) where T : System.IComparable<T> {
 3          return a.CompareTo(b) > 0 ? a : b;
 4      }
 5      class Widget { }
 6      static void Main(string[] args) {
 7          System.Console.WriteLine(Max(new Widget(), new Widget()));
                                                      ^
 8      }
 9  }
Program.cs(7,34): error CS0311: The type 'Program.Widget' cannot be used as type parameter 'T' in the generic type or method 'Program.Max<T>(T, T)'. There is no implicit reference conversion from 'Program.Widget' to 'System.IComparable<Program.Widget>'.

MaxはIComparable<T>を実装した型しか受け取れません。

直し方: new Widget(), new Widget() を 4, 6 にします。

この問題を解いてみる →

パターン5

 1  class Program {
 2      static bool IsGreater<T>(T a, T b) where T : System.IComparable<T> {
 3          return a.CompareTo(b) > 0;
 4      }
 5      class Marker { }
 6      static void Main(string[] args) {
 7          System.Console.WriteLine(IsGreater(new Marker(), new Marker()));
                                                            ^
 8      }
 9  }
Program.cs(7,34): error CS0311: The type 'Program.Marker' cannot be used as type parameter 'T' in the generic type or method 'Program.IsGreater<T>(T, T)'. There is no implicit reference conversion from 'Program.Marker' to 'System.IComparable<Program.Marker>'.

独自クラスにIComparable<T>の実装がなければ型引数として使えません。

直し方: new Marker(), new Marker() を 5, 2 にします。

この問題を解いてみる →

よくある誤解

ジェネリックの制約違反は実行時ではなくコンパイル時に検出されます。型引数を明示していなくても、コンパイラは呼び出しから逆算して制約をチェックします。

実務での勘所

C#のジェネリクスは、Javaとは根本的に実装方式が違います。Javaのジェネリクスはコンパイル後に型情報が消える型消去方式ですが、C#(.NET)のジェネリクスは実行時にも型引数の情報を保持する具体化(reification)方式です。この違いのおかげで、C#ではtypeof(T)で実行時に型引数そのものを取得したり、int型のようなvalue型を使うList<int>を、ボックス化なしに専用のメモリレイアウトで効率よく実行したりできます。ジェネリクスの制約違反を厳密にチェックできるのも、この「型情報が実行時まで残る」設計に支えられています。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)