まえおき
C#布教活動しているなかで、初心者ごろしなのがコンパイルエラーな気がしてきたので、トラブルシューティング的なものを書いてみたい。
結果、思ってたのと違う記事が出来上がった。
というわけで、読み物としてお楽しみください。
やってること
わざとコンパイルが通らないコードを書いて、コンパイルエラーを潰していく様を書いてみる。
※この記事全体でいえることですが、別なコードを書いている場合は、同じコンパイルエラーでも、とるべき対処法は変わります。
結局のところ
場当たり的にコンパイルエラー潰すより、文法を学ぶなり、まっとうなサンプルコードを見るなりしたほうが、トータルでみると早くて、まともな設計ができるようになると思うので、この記事は意味がない。ちゃんと勉強しましょう。
環境
Windows10 で、コマンドプロンプトで実行。
この記事では、パスが通せていることを前提にしています。
where csc
の結果:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe
本題に入る前に - Visual Studio向けのソースコードをもってきたケース
'InitializeComponent' は現在のコンテキスト内に存在しません
というコンパイルエラーがでる場合。
InitializeComponent
は、通常、Visual Studioを使っている前提のソースコードです。
(Form
のコードなどがツールで自動生成されたものです。)
コマンドプロンプト環境でコンパイルするのはハードル高いです。
この記事を見ても解決できません。ごめんなさい。
本題 - Step.0 - べた書き
Hello Worldをやりたくて下記みたいなコードを書いたとします。
(ふつうはコンパイル通るコードをコピペしてHello Worldやると思いますが。)
WriteLine(HelloWorld);
csc BadExample.cs
と打ってコンパイルすると、下記のようにおこられます。
BadExample.cs(1,1): error CS0116: A namespace cannot directly contain members such as fields or methods
■日本語の場合:
badExample.cs(1,1): error CS0116: 名前空間にフィールドやメソッドのようなメンバーを直接含めることはできません。
C#で処理を書くときはクラスで囲めということです。
※「うっかり」以外でこのエラーが出るようなコーディングをしている場合は、いますぐコンパイル作業をやめてC#の文法を学びましょう。
(まずは、長くても数十行くらいのコンパイルが通るサンプルコードを持ってきて、なんとなくの書き方を知るのがよいかと。)
Step.1 - コンパイルエラーが増えることもある
Step.0に対してクラスで囲んで対処。
class {
WriteLine(HelloWorld);
}
↓コンパイルエラー
BadExample.cs(1,7): error CS1001: Identifier expected
BadExample.cs(2,5): error CS1520: Method must have a return type
BadExample.cs(2,25): error CS1001: Identifier expected
■日本語の場合:
BadExample.cs(1,7): error CS1001: ID がありません。
BadExample.cs(2,5): error CS1520: メソッドは戻り値の型を持たなければなりません。
BadExample.cs(2,25): error CS1001: ID がありません。
`ファイル名.cs(行番号,文字位置): error CS1518: Expected class, delegate, enum, interface, or struct`
むしろコンパイルエラーが増えました。1つ潰すとコンパイルエラーが増えることはよくあります。
なお、慣れないうちはコンパイルエラーは先頭から潰しましょう。
エラーメッセージのフォーマットは、
ファイル名(行番号,文字位置): error エラー番号: エラーの説明
です。
特に「行番号」は大きな手掛かりになります。
Step.2
Step.1への対策として、ID(いわゆる識別子)を付ける。
class BadExample {
WriteLine(HelloWorld);
}
コンパイルしてみます。
すると、下記コンパイルエラーとなります。
BadExample.cs(2,5): error CS1520: Method must have a return type
BadExample.cs(2,25): error CS1001: Identifier expected
■日本語の場合:
BadExample.cs(2,5): error CS1520: メソッドは戻り値の型を持たなければなりません。
BadExample.cs(2,25): error CS1001: ID がありません。
Step.3 - コンパイラの解釈とプログラマの意図がずれている
Step.2のコンパイルエラーは、
「このコードを書いた人はきっと『BadExample
クラスがWriteLine
メソッドを持っているコードを書きたい。』のだろう。」と解釈してエラーメッセージをだしています。
class BadExample {
戻り値の型 WriteLine(引数) {
:
}
}
実際にやりたいことは、WriteLine
を呼び出して「Hello World」を表示したい。(この記事ではそういう設定と思ってください。)
なので、下記のように修正してみます。
class BadExample {
void BadMethod() {
WriteLine(HelloWorld);
}
}
コンパイルしてみます。
すると、下記コンパイルエラーとなります。
BadExample.cs(3,9): error CS0103: The name 'WriteLine' does not exist in the current context
BadExample.cs(3,19): error CS0103: The name 'HelloWorld' does not exist in the current context
■日本語の場合:
BadExample.cs(3,9): error CS0103: 名前 'WriteLine' は現在のコンテキスト内に存在しません。
BadExample.cs(3,19): error CS0103: 名前 'HelloWorld' は現在のコンテキスト内に存在しません。
エラーが、「〇〇は現在のコンテキスト内に存在しません。」だけになりました。
ようやく構文エラーが消えました。
構文エラーの場合、直接の原因でない箇所でもコンパイルエラーがでたりするので、まずは構文エラーを潰すのが定石だと思います。(括弧の閉じ忘れとか、識別子が1個抜けてるとか、セミコロンを忘れているとかとか。)
Visual Studio Codeなどの、構文をある程度解釈して色分け表示してくれるようなエディタや統合開発環境を選べば、うっかり系の構文エラーはかなり減らせます。
Step.4 - クラスを探す(Microsoft Docsがおすすめ)
Step.3では、コンパイラがWriteLine
がないと言っているので、検索キーワードwriteline site:microsoft.com
とかでググるなりします。
(今回のように、クラスを探すだけならsite:microsoft.com
を付けるのがおすすめです。)
「Console.WriteLine - Microsoft Docs」
https://docs.microsoft.com/ja-jp/dotnet/api/system.console.writeline?view=netframework-4.8
が見つかるはずなので、開きます。(Console
を知っているというテイで、ご容赦ください。。)
名前空間:System
Assemblies:System.Console.dll, mscorlib.dll, netstandard.dll
とかかれているはずです。(別のクラスにもWriteLine
があるので注意。)
というわけで、名前空間をつなげてSystem.Console.WriteLine
としてあげると、コンパイラがこれを特定できます。
(なお、using 名前空間;
と宣言しておくと、名前空間.
は省略できます。
※Console
はクラス名なので省略できません1。)
class BadExample {
void BadMethod() {
System.Console.WriteLine(HelloWorld);
}
}
コンパイルしてみます。
すると、下記コンパイルエラーとなります。
BadExample.cs(3,34): error CS0103: 名前 'HelloWorld' は現在のコンテキスト内に存在しません。
Step.5 - 文字列について(ちょっと脱線)
文字列は""
で囲む。(記事を書いている途中で、このミスは「わざとらしすぎる」ので、最初から"HelloWorld"にしようかと思ったが、警告内容が変わってめんどくさいので、そのままにした。)
ちなみに@"xxx"
とすると、エスケープ文字を処置しなくて済むので、ファイルパスとかを埋め込むときとか正規表現書くときはこっちのほうが楽。
ダメな例:"c:\tmp.txt"
(※\t
はタブ文字と解釈される。\t
に限らず、基本的にエスケープをミスっててもコンパイルは通ってしまうケースが多い。)
OKな例:"c:\\tmp.txt"
もしくは @"c:\tmp.txt"
エスケープ文字や"
を含まない文字列については、@
付きの表記のほうがミスしにくいのでお勧めしたい。
class BadExample {
void BadMethod() {
System.Console.WriteLine(@"HelloWorld");
}
}
コンパイルしてみます。
すると、下記コンパイルエラーとなります。
error CS5001: Program 'c:\(中略)\BadExample.exe' does not contain a static 'Main' method
suitable for an entry point
■日本語の場合:
error CS5001: プログラム 'c:\(中略)\BadExample.exe' は、エントリ ポイントに適切な静的
'Main' メソッドを含んでいません
Step.6 - Mainメソッドがないとプログラムの入口がわからない
Main
メソッドがないよ。
※C言語ユーザーは違和感あるかもしれませんが、mainではなくMainです。
ググってC#のMainの書き方を調べたことにしてください。
[STAThread]
をつけ忘れてハマると可哀想なので、つけておきます。
class BadExample {
[STAThread]
static void Main() {
System.Console.WriteLine(@"HelloWorld");
}
}
これでいけるでしょ。完成!
↓コンパイルエラー
BadExample.cs(2,6): error CS0246: The type or namespace name 'STAThread' could not be found (are you missing a using
directive or an assembly reference?)
BadExample.cs(2,6): error CS0246: The type or namespace name 'STAThreadAttribute' could not be found (are you missing a
using directive or an assembly reference?)
■日本語の場合:
BadExample.cs(2,6): error CS0246: 型または名前空間名 'STAThread' が見つかりませんでした。using
ディレクティブまたはアセンブリ参照が不足しています。
BadExample.cs(2,6): error CS0246: 型または名前空間名 'STAThreadAttribute' が見つかりませんでした。using
ディレクティブまたはアセンブリ参照が不足しています。
Step.7
ディレクティブまたはアセンブリ参照が不足しています。
は、名前空間の指定が足りていなくて、コンパイラが見つけられていないケースが多いです。
※コンパイルオプションが必要な場合もあります。2
site:microsoft.com
をつけてググります。
STAThreadAttribute Class
名前空間:System
Assemblies:System.Runtime.dll, mscorlib.dll, netstandard.dll
System
に存在しているので、
using System;
class BadExample {
[STAThread]
static void Main() {
Console.WriteLine(@"HelloWorld");
}
}
という風にすればコンパイル通ります。
下記でも問題ないですが、STAThread
にSystem
つけているのはあまり見ないスタイル(※個人の感想です)。
class BadExample {
[System.STAThread]
static void Main() {
System.Console.WriteLine(@"HelloWorld");
}
}
BadExample.exe
の実行結果:
HelloWorld
もうBadExampleじゃないけど、名前変えるとミスしそうなのでこのまま。
・・・見てくれた方はありがとうございます。お疲れさまでした!
おまけ - cscの出力メッセージを英語にする。
コンパイラのメッセージを英語と日本語の両方を載せたいと思い、調べた。
コマンドプロンプトで chcp 437
を実行するとcscの警告内容も英語に変えられる。日本語(Shift_JIS)はchcp 932
。
参考:
http://pocketstudio.jp/log3/2012/02/13/change_language_and_chcp/
https://www.ipentec.com/document/windows-codepage-list
-
「省略できません」はちょっと嘘ですが、この記事を読む段階では知らないほうがよい気がします。(
using static
使えば、static
なクラスについては省略可能になる。Math
クラスとか使いまくる場合はいりようになるかもしれない。) ↩ -
csc
の/r:DLLへのパス
オプションでアセンブリ参照を追加しないといけないケースがあります。(Excel使う場合とか。大体はC:\Windows\assembly\
以下をdir /s 名前空間名.dll
すればでてくると思います。参考: https://www.atmarkit.co.jp/fdotnet/dotnettips/846extratfromgac/extratfromgac.html ) ↩