こんにちは、Streetw☆です。
とりこびとさんの「その1」のコメント欄で凹んだから、「もう、とりこびとさんところではコメントしないっっ」と思ったので、エントリさせてもらうことにしました♪
とりこびとさんのエントリでは「Visual Basic における」と書かれているので、その1~3について
「じゃ、C#ではどうなの?」
って思った方も沢山いらっしゃると思います。
そんな多くの方々の中から今回は特別、かるぼさんのため・だ・け・に調べてみました~
・・・というエントリを妄想しながら下の内容も勢いで書いちゃったので、思い切ってとりこびとさんにエントリをお願いしました。そしたら実はとりこびとさんもC#の結果を書かれる予定だったみたいなんですが、快諾していただきました。ありがとうございます。無理言っちゃってごめんなさい。
では、本題に入ります。
★その1
「1. ジェネリック パラメータと ParamArray パラメータを使用した場合の オーバーロードの解決動作が異なる。」
の、C#での動作について。
C#の状況は、Visual Basicの状況と同じでした。C#でも、Visual Studio 2008で仕様が変更されていたんですね。
試したコードは次の通りです。
Visual Studio 2005では "A"、Visual Studio 2008では "B" が表示されます。
public class Class1
{
public void Method<T>(T a, params int[] b)
{
MessageBox.Show("A");
}
public void Method<T>(T a, T b)
{
MessageBox.Show("B");
}
}
private void button1_Click(object sender, EventArgs e)
{
int i = 0;
short s = 0;
Class1 c = new Class1();
c.Method(i, s);
}
C#って Visual Basicよりは互換性に注意を払ってるような気がしていたので、この結果には驚きでした。
#変な偏見とかじゃなくて、言語の方向性がそうなのかなって思ってました。
Visual Studio 2008では、「対象のフレームワーク」でバージョンを指定できるけど、この動作には影響しませんでした。
ちなみに「その2」の方もですが、推論にまかせずに、
c.Method<int>(i, s);
と書けば、Visual Studio 2005でも同じ結果が得られます。
★その2
「2. ジェネリック クラスと非ジェネリック クラスが存在する場合の オーバーロードの解決動作が異なる。」
の、C#での動作について。
C#の状況は、Visual Basicの状況と異なりました。C#には仕様の変更はなくて、Visual Studio 2005と2008のどちらの場合でも、ジェネリック型に制約が指定された方("D" が表示される方)のメソッドが呼び出されました。
試したコードは次の通りです。
Visual Studio 2008では、Visual Basicではエラーになりますが、C#ではエラーにはならないのです。
public class Class1
{
public void Method<T>(T arg)
{
MessageBox.Show("C");
}
public void Method<T>(T? arg) where T : struct
{
MessageBox.Show("D");
}
}
public class Class2<A>
{
public void Method<T>(T arg)
{
MessageBox.Show("C");
}
public void Method<T>(T? arg) where T : struct
{
MessageBox.Show("D");
}
}
private void button1_Click(object sender, EventArgs e)
{
int? n = null;
Class1 c1 = new Class1();
c1.Method(n);
Class2<string> c2 = new Class2<string>();
c2.Method(n);
}
言語仕様を「C# Language Specification 2.0.doc」で確認すると、「20.1.8 ジェネリック クラスのオーバーロード」に次のように書かれていました(抜粋)。
「型引数の置換によって同じシグニチャが生成される可能性があります。同じシグニチャが生成された場合は、オーバーロード解決の決定規則によって最も限定的なメンバが選択されます。」
想像ですが、ジェネリック型に制約がかかっている方が「最も限定的」と考えられるんじゃないかな?って思いました。「オーバーロード解決の決定規則」の詳細は、「20.9 検索規則の変更」に書かれてることかもしれないけど、ややこしくてわかりませんでした…orz
どうしてVisual Basicの方ではエラーにされたんでしょうか?
人にとってわかりづらいことはVisual Basicではダメってことでしょうか~
★その3
「3. T から T? への拡張が定義済みの変換として扱われる。」
の、C#での動作について。
C#の状況は、Visual Basicの状況と異なりました。C#には仕様の変更はなくて、Visual Studio 2005と2008のどちらの場合でも、コンパイルエラーになりました。
試したコードは次の通りです。
Visual Studio 2008では、Visual Basicの仕様が変更になった結果、C#の仕様と同じになったんですね。
public class Class1
{
public void Method(int arg)
{
MessageBox.Show("E");
}
public void Method(short? arg)
{
MessageBox.Show("F");
}
}
private void button1_Click(object sender, EventArgs e)
{
Class1 c1 = new Class1();
short s = 0;
c1.Method(s);
}
渡すのはshort型なんだから "F" が表示されてもいいのにと思ってしまいました。short→short?と、short→intの変換は、同じ扱いなんでしょうか。
ところで、このときのエラーメッセージって、
「次のメソッドまたはプロパティ間で呼び出しが不適切です」
なんですが、それのヘルプでは
「コンパイラ エラー CS0121:次のメソッドまたはプロパティ間の呼び出しがあいまいです」
となっていて、ちょっとだけ違います。どちらかというと「不適切」より「あいまい」の方がわかりやすいかな~
その点、Visual Basicでのエラーメッセージはとてもわかりやすいですね♪
★C#についてのまとめ
移行時に気をつけないといけないのは、C#では「その1」だけでした。
| その1 | その2 | その3 |
VS2008での仕様の変更 | あり | なし | なし |
VS2008でのVBとの相違 | なし | あり | なし |
★番外♪
上のことを調べていて気付いたことがあったので、書かせてもらいます。この話は、Visual Studio 2008でC#を使った場合の話です。
#Visual Basicではこうなりませんでしたが、別の行に移動してからメソッドにマウスを合わすと、ヒント上の型パラメータのところにはちゃんと推論された型が表示されました。
#Visual Studio 2005では言語に関係なく、こうはなりませんでした。
次の画像は上記の「その2」のコードで、これから変数 "n" を入力しようとしてるところです。
#変数名が違うのは気にしちゃダメですw
この時点ではまだ型は推論できないから、パラメータヒント上の型パラメータは "T" のままになってます。
でも、"n" を入力して確定する(インテリセンスを閉じる)と、次の画像のようにヒントの内容が変わります。
型パラメータとして int 型が推論され、"D" が表示される方のメソッドに決定されたことがわかりますよね。
この推論では、ちゃんとNullableの中身を見てくれたんですね~
このあとにドットを続けて入力すると型パラメータは "T" に戻り、それからValueプロパティを選択すると次の画像のようになります。
引数の方が "int?" になっていないので、今度は "C" が表示される方のメソッドに決定されたことがわかります。
もし、"D" が表示される方のメソッドをコメントにして "n" の入力に戻すと、そのときはもちろん型パラメータはint?型に推論されます。
なんか凄いですよね!
それではこれで失礼します。おじゃましました~