この記事では、C#プログラミング言語の構文について説明します。説明されている機能は、.NET FrameworkおよびMonoと互換性があります。
基礎
識別子
識別子は、コード内の要素の名前です。識別子には、文字、数字、アンダースコア( _) を含めることができ、大文字と小文字が区別されます(FOOは とは異なりますfoo)。言語では、識別子名に次の制限が課せられます。
- 数字で始まることはできません。
- キーワードでない限り、シンボルで始めることはできません。
- 511文字を超えることはできません。
識別子名の前にアットマーク( @) が付く場合もありますが、これは重要ではありません。@nameは と同じ識別子ですname。
MicrosoftはC#の識別子の命名規則を公開しており、型名とほとんどの型メンバーにはパスカルケースを使用し、変数とプライベートまたは内部フィールドにはキャメルケースを使用することを推奨しています。 [1]ただし、これらの命名規則は言語では強制されていません。
キーワード
キーワードは、特別な構文上の意味を持つ定義済みの予約語です。[2]この言語には、コンテキストキーワードと予約キーワードの 2 種類のキーワードがあります。 またはfalseなどの予約キーワードbyteは、キーワードとしてのみ使用できます。 またはwhereなどのコンテキストキーワードは、from特定の状況でのみキーワードとして扱われます。[3]予約キーワードと同じ識別子が必要な場合は、区別するためにアットマークを前に付けることができます。たとえば、@outは識別子として解釈されますが、 はoutキーワードとして解釈されます。この構文により、他の言語で書かれた.NETコードの再利用が容易になります。[4]
以下のC#キーワードは予約語です: [2]
abstractasbaseboolbreakbytecasecatchcharcheckedclassconstcontinuedecimaldefaultdelegatedodoubleelseenumeventexplicitexternfalsefinallyfixedfloatforforeachgotoifimplicitinintinterfaceinternalislocklongnamespacenewnullobjectoperatoroutoverrideparamsprivateprotectedpublicreadonlyrefreturnsbytesealedshortsizeofstackallocstaticstringstructswitchthisthrowtruetrytypeofuintulonguncheckedunsafeushortusingvirtualvoidvolatilewhile
コンテキストキーワードはコード内で特定の意味を与えるために使用されますが、C#では予約語ではありません。やなどの一部のコンテキストキーワードは、partial複数whereのコンテキストで特別な意味を持ちます。次のC#キーワードはコンテキストキーワードです: [5]
addallowsaliasandascendingargsasyncawaitbydescendingdynamicequalsfromgetglobalgroupinitintojoinletmanagednameofnintnotnotnullnuintonororderbypartialrecordremoverequiredselectsetunmanagedvaluevarwhenwherewithyield
リテラル
- ^ C# では文字列はnull で終了しないため、文字列内のどこにでも null 文字が出現する可能性があります。
桁区切り
C# 7.0以降では、読みやすさを考慮して、数値の桁を区切るためにアンダースコア記号を使用できます。コンパイラはこれらのアンダースコアを無視します。 [6]
int bin = 0 b1101_0010_1011_0100 ; int hex = 0 x2F_BB_4A_F1 ; int dec = 1 _000_500_954 ; double real = 1 _500 . 200 _2e - 1 _000 ;
一般的に、これは数字文字の間にのみ置くことができます。_121値の先頭 ( ) や末尾 (121_または121.05_)、浮動小数点値の小数点の隣 ( 10_.0)、指数文字の隣 ( 1.1e_1)、または型指定子の隣 ( 10_f) に置くことはできません。
変数
変数は値に関連付けられた識別子です。変数の型と名前を記述して宣言され、同じステートメントでオプションで初期化されます。[10] [11]
宣言する
int myInt ; // 'int' 型の 'myInt' という初期化されていない変数を宣言します
割り当て
int myInt ; // 初期化されていない変数を宣言するmyInt = 35 ; // 変数に値を割り当てる
初期化
int myInt = 35 ; // 変数の宣言と初期化
同じ型の複数の変数を 1 つのステートメントで宣言および初期化できます。
int a , b ; // 同じ型の複数の変数を宣言する
int a = 2 , b = 3 ; // 同じ型の複数の変数を宣言して初期化する
ローカル変数の型推論
- これはC# 3.0の機能です。
C# 3.0 では型推論が導入され、実際の型が初期化子から静的に決定できる場合、変数宣言の型指定子をキーワード に置き換えることができるようになりましたvar。これにより、特に複数のジェネリック型パラメータを持つ型の繰り返しが削減され、DRY原則にさらに厳密に準拠します。
var myChars = new char [] { 'A' , 'Ö' }; // または char[] myChars = new char[] {'A', 'Ö'};
var myNums = new List < int > (); // または List<int> myNums = new List<int>();
定数
定数は不変の値です。
const
キーワードをプレフィックスとしてローカル変数またはフィールドを宣言する場合const、宣言時に値を指定する必要があります。その後はロックされ、変更できません。コンテキスト内でフィールドまたはローカル変数として宣言できます。[10]定数は暗黙的に静的です。
定数倍PI = 3.14 ;
これはキーワードの両方の用法を示しています。
パブリッククラスFoo {プライベートconst double X = 3 ;
パブリックFoo () { const int y = 2 ; } }
readonly
キーワードreadonlyはフィールドに対しても同様のことを行います。 とマークされたフィールドと同様に、const初期化後は変更できません。違いは、コンストラクタで初期化するか、実行時までわからない値に初期化するかを選択できることです。[12]これはフィールドに対してのみ機能します。readonlyフィールドはインスタンスのメンバーまたは静的クラスのメンバーのいずれかになります。
コードブロック
中括弧は、コード ブロックと新しいスコープを示すために使用されます。クラス メンバーとメソッドの本体は、さまざまなコンテキストでこれらの中括弧内に存在する可能性のあるものの例です。
{ ... }
メソッド本体内では、中括弧を使用して新しいスコープを作成できます。
void DoSomething () { int a ;
{
int b ; a = 1 ; }
a = 2 ; b = 3 ; // 変数が内部スコープで宣言されているため失敗します。}
プログラム構造
C# アプリケーションは、クラスとそのメンバーで構成されます。クラスやその他の型は名前空間に存在しますが、他のクラス内にネストすることもできます。
主な方法
コンソールでもグラフィカル インターフェイス アプリケーションでも、プログラムには何らかのエントリ ポイントが必要です。C# アプリケーションのエントリ ポイントはメソッドですMain。このメソッドの宣言は 1 つだけであり、クラス内の静的メソッドです。通常は、void文字列の配列としてコマンド ライン引数を返し、渡します。
static void Main ( string [] args ) // プログラムにコマンドライン引数がない場合は、string[] args を省略できます。{ }
メイン メソッドでは、指定されている場合は整数値を返すこともできます。
静的int Main (文字列[]引数) { 0を返す; }
非同期メイン
これは C# 7.1 の機能です。
Mainメソッドの戻り値の型を として宣言することにより、メソッド内で非同期タスクを待機できますTask。
静的非同期タスクメイン(文字列[]引数) { DoWorkAsync ( 42 )を待機します。 }
Task、またはとTask<int>,、パラメータの有無にかかわらず、すべての組み合わせがstring[] argsサポートされています。
トップレベルのステートメント
これは C# 9.0 の機能です。
Programスクリプト言語と同様に、トップレベルのステートメントでは、メソッドを使用してクラスを宣言する手順が不要になりますMain。
代わりに、ステートメントを 1 つの特定のファイルに直接記述することができ、そのファイルがプログラムのエントリ ポイントになります。他のファイルのコードは、クラスで定義する必要があります。
これは、C# の冗長性を減らし、初心者が使いやすくするために導入されました。
システムの使用;
コンソール.WriteLine ( "Hello World!" ) ;
型はステートメントの後に宣言され、その上のステートメントから自動的に使用できるようになります。
名前空間
名前空間は型名の一部であり、名前付きエンティティをグループ化したり、他のエンティティと区別したりするために使用されます。
System . IO . DirectoryInfo // DirectoryInfo は System.IO 名前空間にあります
名前空間は次のように定義されます。
名前空間FooNamespace { // メンバー}
名前空間は次のように名前空間内に配置できます。
namespace FooNamespace { // メンバーnamespace BarNamespace { // メンバー } }
次のようにすることもできます:
名前空間FooNamspace { // メンバー}
名前空間FooNamspace.BarNamespace { // メンバー}
C# 10以降では、次のようにファイルスコープ宣言を使用して名前空間を定義することもできます。 [13]
namespace FooNamespace ; // ここではセミコロンの代わりに角括弧が省略されています。
using指令
ディレクティブusingは、参照されたアセンブリから特定の名前空間を読み込みます。通常はコード ファイルの先頭 (またはヘッダー) に配置されますが、必要に応じて他の場所 (クラス内など) に配置することもできます。[引用が必要]
Systemを使用します。System.Collectionsを使用します。
このディレクティブは、既存の名前空間または型に別の名前を定義するためにも使用できます。名前が長すぎて読みにくい場合に役立つことがあります。
Net = System.Netを使用します。DirInfo = System.IO.DirectoryInfoを使用します。
using static指令
ディレクティブは、指定された型の静的メンバーを現在のスコープに読み込み、メンバーの名前で直接アクセスできるようにします。using static
静的System.Consoleを使用します。
WriteLine ( "Hello, World!" );
オペレーター
演算子のオーバーロード
既存の演算子の一部は、オーバーロード メソッドを記述することでオーバーロードできます。
パブリック静的Foo演算子+ ( Foo foo 、Bar bar ) { return new Foo ( foo . Value + bar . Value ); }
- 代入演算子(など) は、二項演算子と代入演算子 ( ) の組み合わせであり、オーバーロード可能な通常の演算子を使用して評価されます。
+=, *== - キャスト演算子( ) はオーバーロードできませんが、変換演算子を定義することは可能です。
( ) - 配列インデックス(
[ ]) 演算子はオーバーロードできませんが、新しいインデクサーを定義することは可能です。
変換演算子
キャスト演算子はオーバーロードできませんが、ターゲット クラス内に存在する変換演算子メソッドを記述できます。変換メソッドでは、暗黙的変換演算子と明示的変換演算子の 2 種類の演算子を定義できます。暗黙的演算子はキャスト演算子 ( ) を指定せずにキャストしますが、明示的演算子ではキャスト演算子を使用する必要があります。
( )
暗黙の変換演算子
クラスFoo { public int Value ;
public static implicit operator Foo ( int value ) { return new Foo ( value ); } } // 暗黙的な変換Foo foo = 2 ;
明示的な変換演算子
クラスFoo { public int Value ;
public static explicit operator Foo ( int value ) { return new Foo ( value ); } } // 明示的な変換Foo foo = ( Foo ) 2 ;
asオペレーター
演算子asは、指定された型へのサイレント キャストを試みます。可能な場合はオブジェクトを新しい型として返し、そうでない場合は null を返します。
Stream stream = File.Open ( @"C:\ Temp \data.dat" ); FileStream fstream = stream as FileStream ; //オブジェクトを返します。
String str = stream as String ; // null を返します。
ヌル合体演算子
- これはC# 2.0の機能です。
次の:
ifNotNullValueを返します。そうでない場合は Valueを返します。
は次の略語です:
ifNotNullValue != nullを返します。ifNotNullValue : elseValue ;
つまり、変数の内容ifNotNullValueが null でない場合はその内容が返され、そうでない場合は変数の内容otherwiseValueが返されます。
C# 8.0ではnull結合代入が導入され、
変数?? = elseValue ;
は、
if (変数がnull )変数= elseValue ;
制御構造
C# は C/C++ の制御構造のほとんどを継承し、foreachステートメントなどの新しい制御構造も追加します。
条件文構造
これらの構造は、与えられた条件を通じてプログラムの流れを制御します。
if声明
if指定された条件が真の場合にステートメントが開始されます。単一行の case ステートメントではブロック中括弧は必要ありませんが、慣例によりブロック中括弧が推奨されます
。
シンプルな一行のステートメント:
i == 3の場合... ;
else-block を使用した複数行 (中括弧なし):
( i == 2 )の場合...それ以外の場合...
if ステートメントの推奨コーディング規則。
i == 3の場合... } i == 2の場合... }そうでない場合... }
switch声明
このswitch構造は、さまざまな値のフィルターとして機能します。各値は「ケース」につながります。ケース セクションを経由することは許可されていないため、キーワードは通常、ケースを終了するために使用されます。ケース セクションのbreak無条件も、ケースを終了するために使用できます。ステートメントを使用して、1 つのケースから次のケースに経由する方法も参照してください。ただし、多くのケースが同じコードにつながる可能性があります。デフォルトのケースは、構造によって処理されない他のすべてのケースを処理します。
returngoto
switch ( ch ) { case 'A' : statement ; ... break ; case 'B' : statement ; break ; case 'C' : // switch セクションには複数の case ラベルを含めることができます。case 'D' : ... break ; default : ... break ; }
反復構造
反復ステートメントは、指定された条件が true と評価されたときに繰り返し実行されるステートメントです。
whileループ
while ( i == true ) { ... }
do ... whileループ
する
{
}
while ( i == true );
forループ
ループは、宣言、条件、カウンター式forの 3 つの部分で構成されます。これらはオプションなので、省略できます。
( int i = 0 ; i < 10 ; i ++ ) { ... }
これはステートメントで表されるこのコードと同等ですがwhile、ここではi変数がループに対してローカルではありません。
int i = 0 ; while ( i < 10 ) { //... i ++ ; }
foreachループ
ステートメントは ステートメントforeachから派生しfor、C# の言語仕様で説明されている特定のパターンを使用して、反復処理する要素の列挙子を取得して使用します。
指定されたコレクション内の各項目は、コード ブロックのコンテキストで返され、アクセス可能になります。ブロックが実行されると、項目がなくなるまで次の項目が返されます。
foreach ( int i intList ) { ... }
ジャンプステートメント
ジャンプ ステートメントは C/C++ から継承され、最終的にはアセンブリ言語を通じて継承されます。ジャンプ ステートメントは、プログラムの流れを制御するアセンブリ言語のジャンプ命令を表すだけです。
ラベルとgotoステートメント
ラベルには、ステートメントを使用してジャンプできるコード内のポイントが与えられますgoto。
開始:
.......開始へ移動;
ラベルはgotoステートメントの後に配置する必要はなく、ソース ファイル内でステートメントの前に配置することもできます。
このステートメントは、あるケースから別のケースにジャンプしたり、あるケースから次のケースにフォールスルーしたりするステートメント
gotoで使用できます。switch
switch ( n ) { case 1 : Console.WriteLine ( "Case 1" ) ; break ; case 2 : Console.WriteLine ( " Case 2" ) ; goto case 1 ; case 3 : Console.WriteLine ( "Case 3" ) ; case 4 : // C #ではケースをフォールスルーできないため、ここでコンパイルが失敗します。Console.WriteLine ( " Case 4" ); goto default ; // これは次のケースにフォールスルーする正しい方法です。case 5 : // 同じコードに複数のラベルがあっても問題ありません。 case 6 : default : Console.WriteLine ( " Default " ) ; break ; // defaultでも終了点に到達してはいけません}
break声明
ステートメントbreakは最も近いループまたはswitchステートメントから抜け出します。終了したステートメントの後のステートメントがある場合は、そのステートメントで実行が継続されます。
int e = 10 ; for ( int i = 0 ; i < e ; i ++ ) { while ( true ) { break ; } // この時点で中断します。}
continue声明
このcontinueステートメントは、現在の制御ステートメントの現在の反復を中止し、次の反復を開始します。
int ch ; while (( ch = Console.Read ()) != - 1 ) { if ( ch == ' ' ) continue ; // whileループの残りをスキップします
// while ループの残り
... }
while上記のコードのループは、 を呼び出して文字を読み取り、文字がスペースの場合はループ本体のステートメントをスキップします。
GetChar()
例外処理
C# のランタイム例外処理メソッドは、Java および C++ から継承されます。
基本クラス ライブラリには、他のすべての例外クラスが派生するというクラスがあります。 -object には、特定の例外に関するすべての情報と、発生した内部例外も含まれています。プログラマーは、クラスから派生することで独自の例外を定義できます。
System.ExceptionExceptionException
例外は次のようにスローされます:
新しいNotImplementedException ()をスローします。
try ... catch ... finally声明
例外はブロック内で管理されます。
try ... catch
try
{
// 例外をスローする可能性のあるステートメント... } catch ( Exception ex ) { // ここで例外がキャッチされ、処理されます... } finally { // try/catch ブロックの後に常に実行されるステートメント... }
ブロック内のステートメントtryが実行され、いずれかのステートメントが例外をスローすると、ブロックの実行は中止され、例外はブロックによって処理されますcatch。ブロックが複数あるcatch場合、スローされた例外の型と一致する型の例外変数を持つ最初のブロックが実行されます。
catchスローされた例外の型に一致するブロックがない場合、try ... catchステートメントを含む外側のブロック (またはメソッド) の実行は中止され、例外は包含ブロックまたはメソッドの外側に渡されます。例外は、現在アクティブなメソッドの 1 つ内で一致するブロックが見つかるまで、コール スタックcatchを通じて上方に伝播されます。一致するブロックが見つからないまま、例外が最上位のメソッドまで伝播した場合は、プログラム全体が終了し、例外のテキスト記述が標準出力ストリームに書き込まれます。
Main()catch
ブロック内のステートメントは、例外がスローされたかどうかに関係なく、
finally常にブロックtryとブロックの後に実行されます。このようなブロックは、クリーンアップ コードを提供するのに役立ちます。catch
ブロックの後にcatch、ブロック、finallyブロック、またはその両方を続ける必要がありますtry。
種類
C# は、C や C++ と同様に静的に型付けされた言語です。つまり、すべての変数と定数は、宣言時に固定された型を取得します。型には、値型と参照型の 2 種類があります。
値の種類
値型のインスタンスはスタック上に存在します。つまり、それらは変数にバインドされています。値型の変数を宣言すると、メモリが直接割り当てられます。変数がスコープ外になると、オブジェクトもそれとともに破棄されます。
構造
構造体は、一般的にはstructとして知られています。構造体は、キーワードを使用して宣言されるユーザー定義の値型ですstruct。クラスと非常に似ていますが、軽量型に適しています。クラスと構造体の重要な構文上の違いについては、この記事の後半で説明します。
構造体Foo { ... }
プリミティブ データ型はすべて構造体です。
定義済みタイプ
これらはプリミティブ データ型です。
注意: string ( ) は構造体ではなく、プリミティブ型でもありません。
System.String
列挙
列挙型( で宣言enum) は整数値を表す名前付きの値です。
enum Season { Winter = 0 、Spring = 1 、Summer = 2 、Autumn = 3 、Fall = Autumn // アメリカ英語では Autumn は Fall と呼ばれます。}
列挙変数は、デフォルトでゼロに初期化されます。列挙型によって定義された名前付き値に割り当てたり初期化したりできます。
季節シーズン;シーズン=シーズン。春;
列挙型変数は整数値です。同じ型の変数間の加算と減算は、特定のキャストなしで実行できますが、乗算と除算は多少リスクが高く、明示的なキャストが必要です。列挙型変数を整数型に変換したり、整数型から列挙型変数に変換したりする場合もキャストが必要です。ただし、型定義で値が指定されていない場合、キャストは例外をスローしません。
season = ( Season ) 2 ; // 2 を Season 型の列挙値にキャストします。season = season + 1 ; // 値に 1 を加算します。season = season + season2 ; // 2 つの列挙変数の値を加算します。int value = ( int ) season ; // 列挙値を整数値にキャストします。
season ++ ; // Season.Spring (1) は Season.Summer (2) になります。season -- ; // Season.Summer (2) は Season.Spring (1) になります。
値はビット OR 演算子を使用して結合できます|。
色myColors =色.緑|色.黄|色.青;
参照タイプ
参照型用に作成された変数は、型付けされた管理参照です。コンストラクターが呼び出されると、ヒープ上にオブジェクトが作成され、変数に参照が割り当てられます。オブジェクトの変数がスコープ外になると参照が壊れ、参照が残っていない場合はオブジェクトがガベージとしてマークされます。その後、ガベージ コレクターがすぐにそれを収集して破棄します。
参照変数は、どのオブジェクトも参照していない場合は null になります。
配列
配列型は、特定の型の 1 つ以上の要素を含む空間を参照する参照型です。すべての配列型は共通の基本クラスから派生します。各要素は、C++ や Java と同様に、インデックスによって参照されます。
System.Array
C# の配列は、C++ では 動的配列と呼ばれます。
int [] numbers = new int [ 2 ]; numbers [ 0 ] = 2 ; numbers [ 1 ] = 5 ; int x = numbers [ 0 ];
初期化子
配列初期化子は、配列の初期化に便利な構文を提供します。
// 長い構文
int [] numbers = new int [ 5 ]{ 20 , 1 , 42 , 15 , 34 }; // 短い構文int [] numbers2 = { 20 , 1 , 42 , 15 , 34 }; // 推論された構文var numbers3 = new [] { 20 , 1 , 42 , 15 , 34 };
多次元配列
配列は複数の次元を持つことができます。たとえば、グリッドを表す 2 つの次元などです。
int [,]数値=新しいint [ 3 , 3 ];数値[ 1 , 2 ] = 2 ;
int [,] numbers2 =新しいint [ 3 , 3 ] { { 2 , 3 , 2 }, { 1 , 2 , 6 }, { 2 , 4 , 5 } };
参照
クラス
クラスは、自己記述型のユーザー定義の参照型です。.NET Framework の基本的にすべての型は、構造体や列挙型を含むクラスであり、コンパイラによって生成されたクラスです。クラス メンバーは既定でクラス メンバーになりますが、クラスの外部から参照できるようprivateに宣言したり、クラスの子孫から参照できるように宣言したりできます。
publicprotected
文字列
クラス、または単に は、不変の Unicode 文字のシーケンス ( ) を表します。
System.Stringstringchar
文字列に対して実行されるアクションは常に新しい文字列を返します。
文字列text = "Hello World!" ;文字列substr = text . Substring ( 0 , 5 );文字列[] parts = text . Split ( new char []{ ' ' });
このクラスは、変更可能な「文字列」が必要な場合に使用できます。
System.StringBuilder
var sb = new StringBuilder (); sb . Append ( 'H' ); sb . Append ( "el" ); sb . AppendLine ( "lo!" );
インタフェース
インターフェースは、実際の実装のないメンバー定義を含むデータ構造です。インターフェース型の変数は、このインターフェースを実装するクラスのインスタンスへの参照です。#インターフェースを参照してください。
代表者
C# は、デリゲートの形式で型セーフなオブジェクト指向関数ポインターを提供します。
class Program { // デリゲート型: delegate int Operation ( int a , int b );
静的int Add ( int i1 、int i2 ) { return i1 + i2 ; }
静的int Sub ( int i1 、int i2 ) {戻り値i1 - i2 ; }
static void Main () { // デリゲートをインスタンス化し、メソッドを割り当てます。Operation op = Add ;
// デリゲートが指すメソッドを呼び出します。
int result1 = op ( 2 , 3 ); // 5
op = Sub ; int result2 = op ( 10 , 2 ); // 8 } }
匿名メソッドを使用してデリゲートを初期化します。
加算=デリゲート( int a 、int b ) {戻り値a + b ; };
ラムダ式を使用してデリゲートを初期化します。
加算= ( a , b ) => a + b ;
イベント
イベントは、複数のメソッドを指すことができるポインターです。より正確には、メソッド ポインターを 1 つの識別子にバインドします。したがって、これはデリゲートの拡張と見なすことができます。通常、イベントは UI 開発のトリガーとして使用されます。C #およびその他の共通言語インフラストラクチャで使用される形式は、従来のVisual Basicの形式に基づいています。
デリゲートvoid MouseEventHandler (オブジェクトsender 、MouseEventArgs e );
パブリッククラスButton : System.Windows.Controls.Control {プライベートイベントMouseEventHandler _onClick ;
/* 仮想トリガー関数 */
void Click () { _onClick ( this , new MouseEventArgs ( data )); } }
イベントには、Windows Presentation FoundationやWindows Formsなどのプラットフォーム固有のライブラリでは通常、送信元とイベント引数の2 つのパラメータを受け取る特別なデリゲートから作成されるイベント ハンドラが必要です。イベント引数オブジェクトの型は、CLI 基本ライブラリの一部である EventArgs クラスから派生します。
クラス内で宣言されると、イベントを呼び出す唯一の方法は、所有者の内部からになります。イベントが発生したときにトリガーされるリスナー メソッドを外部に実装できます。
パブリッククラスMainWindow : System.Windows.Controls.Window {プライベートButton _button1 ;
public MainWindow () { _button1 = new Button (); _button1 . Text = "クリックしてください!" ;
/* イベントをサブスクライブします */
_button1 . ClickEvent += Button1_OnClick ;
/* 古いと考えられる代替構文:
_button1.MouseClick += new MouseEventHandler(Button1_OnClick); */
}
protected void Button1_OnClick ( object sender 、MouseEventArgs e ) { MessageBox . Show ( "クリックされました!" ); } }
カスタム イベントの実装も可能です。
プライベートEventHandler _clickHandles = ( s 、e ) => { };
public event EventHandler Click { add { // ハンドラーが追加されたときに実行されるコード... ...
_clickHandles += value ; } remove { // ハンドラーが削除されたときに実行されるコード... ...
_clickHandles -=値; } }
参照
Null 許容型
- これはC# 2.0の機能です。
Null 許容型は、値の型を null にできるようにするために最初に C# 2.0 で導入されました (データベースを操作するときに便利です)。
int? n = 2 ; n = null ;
コンソール.WriteLine ( n.HasValue ) ;
実際には、これは構造体を使用するのと同じです。
Nullable<T>
Null 可能< int > n = 2 ; n = null ;
コンソール.WriteLine ( n.HasValue ) ;
ポインタ
C#には、選択された型(一部のプリミティブ、列挙型、文字列、ポインタ、さらには指し示すことができる型のみを含む配列や構造体[14] )へのポインタがあり、それを安全でないコンテキスト( とマークされたメソッドやコードブロック)で許可します。これらは構文的にはCやC++のポインタと同じです。ただし、ブロック内では実行時チェックは無効になっています。
unsafeunsafe
静的void Main (文字列[]引数) {安全でない{ int a = 2 ; int * b = &a a ;
コンソール. WriteLine ( "a のアドレス: {0}。値: {1}" , ( int ) &a a , a ); Console . WriteLine ( "b のアドレス: {0}。値: {1}。*b の値: {2}" , ( int ) & b , ( int ) b , * b );
// 出力は次のようになります:
// a のアドレス: 71953600。値: 2 // b のアドレス: 71953596。値: 71953600。*b の値: 2 } }
構造体は、文字列やその他のクラスなどのマネージ参照型のメンバーを持たない純粋な構造体である必要があります。
パブリック構造体MyStruct {パブリックchar文字;パブリックint整数; }
パブリック構造体 MyContainerStruct {パブリックbyte Byte ;パブリックMyStruct MyStruct ; }
使用中:
MyContainerStruct x ; MyContainerStruct * ptr = & x ;
バイト値= ptr -> Byte ;
動的
- これはC# 4.0および.NET Framework 4.0の機能です。
型はdynamic、静的な方法で C# への動的なランタイム検索を可能にする機能です。 動的とは、通常のようにコンパイル時に解決されるのではなく、実行時に解決される型のオブジェクトを持つ変数を表します。
この機能は、動的言語ランタイム(DLR)を活用し、 IronPythonやIronRuby ( .NET 向け PythonおよびRubyの実装)などの動的型付け 言語との相互運用を目的として特別に設計されています。
動的サポートにより、COMオブジェクトとの相互運用も容易になります。
dynamic x = new Foo (); x . DoSomething (); // 実行時にコンパイルされ解決されます。無効な場合は例外がスローされます。
匿名型
- これはC# 3.0の機能です。
匿名型は、コンパイラによって生成される名前のないクラスです。匿名型は使用可能ですが、オブジェクトを返す LINQ クエリがありselect、特定の値のみを返したい場合などのシナリオでは非常に便利です。次に、値に対して自動生成された読み取り専用フィールドを含む匿名型を定義します。
同じシグネチャを持つ別の匿名型宣言をインスタンス化すると、その型はコンパイラによって 自動的に推論されます。
var carl = new { Name = "Carl" , Age = 35 }; // 型の名前はコンパイラのみが認識します。var mary = new { Name = "Mary" , Age = 22 }; // 上記の式と同じ型
箱詰めと開封
ボックス化は、値型の値を対応する参照型の値に変換する操作です。 [15] C#でのボックス化は暗黙的です。
アンボックス化とは、参照型(以前にボックス化された)の値を値型の値に変換する操作です。[15] C#でのアンボックス化には明示的な型キャストが必要です。
例:
int foo = 42 ; // 値型。object bar = foo; // foo は bar にボックス化されます。int foo2 = ( int ) bar ; //値型にアンボックス化されます。
オブジェクト指向プログラミング (OOP)
C# はオブジェクト指向プログラミングを直接サポートします。
オブジェクト
オブジェクトは型をテンプレートとして作成され、その特定の型の インスタンスと呼ばれます。
C# では、オブジェクトは参照または値のいずれかです。コード内では、これらを構文的に区別することはありません。
オブジェクトクラス
すべての型、さらにはボックス化された形式の値型も、すべてのオブジェクトの最終的な基本クラスである クラスから暗黙的に継承されます。このクラスには、すべてのオブジェクトで共有される最も一般的なメソッドが含まれています。これらのメソッドの一部はオーバーライド可能です。
System.Objectvirtual
クラスは、直接継承するか、別の基本クラスを介して間接的に継承します。
System.Object
メンバー
Object クラスのメンバーの一部:
Equals- オブジェクト間の比較をサポートします。Finalize- オブジェクトが自動的に再利用される前にクリーンアップ操作を実行します。(デフォルトのデストラクタ)GetHashCode- ハッシュ テーブルの使用をサポートするために、オブジェクトの値に対応する数値を取得します。GetType- 現在のインスタンスの Type を取得します。ToString- クラスのインスタンスを説明する、人間が読めるテキスト文字列を作成します。通常は、型の名前を返します。
クラス
クラスは、 C# などのオブジェクト指向言語の基礎です。クラスの機能はオブジェクトのテンプレートとして機能します。クラスには、現実世界と同じようにデータを保存および操作するメンバーが含まれます。
クラスと構造体の違い
クラスと構造体は、宣言方法と使用方法の両方で似ていますが、いくつかの重要な違いがあります。クラスは参照型で、構造体は値型です。構造体は宣言時にスタックに割り当てられ、変数はそのアドレスにバインドされます。値を直接含みます。クラスが異なっているのは、メモリがヒープ上のオブジェクトとして割り当てられる点です。変数はむしろ、オブジェクトを指すスタック上の管理されたポインターです。これらは参照です。
構造体は、クラスとはいくつかの点で異なります。たとえば、どちらも引数を取らない暗黙のデフォルト コンストラクターを提供しますが、構造体用にそれを再定義することはできません。異なるパラメーター化されたコンストラクターを明示的に定義すると、クラスでは暗黙のデフォルト コンストラクターが抑制されますが、構造体では抑制されません。構造体のすべてのフィールドは、これらの種類のコンストラクターで初期化する必要があります。構造体にはファイナライザーがなく、クラスのように別のクラスから継承することはできません。暗黙的に、構造体はシールされ、( から継承)から継承します。構造体は、少量のデータに適しています。
System.ValueTypeSystem.Object
違いを簡単にまとめると次のようになります。
- ^ 他のコンストラクタが提供されていない場合にのみ生成されます
- ^ 常に自動生成され、プログラマーが書き込むことはできません
宣言
クラスは次のように宣言されます:
class Foo { // メンバー宣言}
部分クラス
- これはC# 2.0の機能です。
部分クラスは、コードが別々のファイルに分割されているクラス宣言です。部分クラスのさまざまな部分は、キーワードでマークする必要がありますpartial。
// File1.cs
部分クラスFoo { ... }
// File2.cs
部分クラスFoo { ... }
部分クラスを使用する一般的な理由は、一部のクラスをプログラマーが保守する部分とツールが保守する部分に分割することです。つまり、一部のコードはユーザー インターフェイス設計ツールなどによって自動的に生成されます。
初期化
クラスのメンバーを使用する前に、オブジェクトへの参照を使用して変数を初期化します。これを作成するには、newキーワードを使用して適切なコンストラクターを呼び出します。これはクラスと同じ名前を持ちます。
var foo =新しいFoo ();
構造体の場合、デフォルトのコンストラクタが自動的に呼び出されるため、明示的にコンストラクタを呼び出す必要はありません。コンストラクタを宣言するだけで、標準の値で初期化されます。
オブジェクト初期化子
- これはC# 3.0の機能です。
オブジェクトのパブリック フィールドとプロパティを初期化するより便利な方法を提供します。デフォルト コンストラクターがある場合、コンストラクターの呼び出しはオプションです。
var person = new Person {名前= "John Doe" 、年齢= 39 };
// 等しい
var person = new Person (); person . Name = "John Doe" ; person . Age = 39 ;
コレクション初期化子
- これはC# 3.0の機能です。
コレクション初期化子は、コレクションを初期化するための配列のような構文を提供します。コンパイラは、Add メソッドへの呼び出しを生成するだけです。これは、インターフェイスを実装するクラスで機能しますICollection。
var list =新しいリスト< int > { 2 , 5 , 6 , 6 };
// 等しい
var list = new List < int > (); list . Add ( 2 ); list . Add ( 5 ); list . Add ( 6 ); list . Add ( 6 );
メンバーへのアクセス
インスタンスのメンバーとクラスの静的メンバーには、.演算子を使用してアクセスします。
インスタンス メンバーへのアクセス
インスタンス メンバーには、変数の名前を通じてアクセスできます。
文字列foo = "Hello" ;文字列fooUpper = foo .ToUpper ( );
静的クラス メンバーへのアクセス
静的メンバーには、クラスの名前または他の型を使用してアクセスします。
int r =文字列.Compare ( foo , fooUpper ) ;
ポインターを介してメンバーにアクセスするアンセーフ コード
では、ポインターによって参照される値 (構造体型) のメンバーは、C および C++ と同様に演算子を使用してアクセスされます。
->
POINT p ; p.X = 2 ; p.Y = 6 ; POINT * ptr = & p ; ptr - > Y = 4 ;
修飾子
修飾子は、型および型メンバーの宣言を変更するために使用されるキーワードです。最も注目すべきは、アクセス修飾子を含むサブグループがあることです。
クラス修飾子
abstract- クラスが基本クラスとしてのみ機能することを指定します。継承クラスで実装する必要があります。クラスに抽象メソッドを許可するための前提条件です。sealed- クラスを継承できないことを指定します。
クラスメンバー修飾子
abstract- すべての派生した非抽象クラスで使用できるメソッドを宣言します。const- 変数が宣言時に初期化する必要がある定数値であることを指定します。event- イベントを宣言します。extern- 本体のないメソッド シグネチャが DLL インポートを使用することを指定します。override- メソッドまたはプロパティの宣言が仮想メンバーのオーバーライドであるか、抽象クラスのメンバーの実装であるかを指定します。readonly- 宣言の一部として、または同じクラスのコンストラクター内でのみ値を割り当てることができるフィールドを宣言します。unsafe- ポインタの使用を許可する、安全でないコンテキストを指定します。virtual- メソッドまたはプロパティの宣言を派生クラスによってオーバーライドできることを指定します。volatile- 外部プロセスによって変更される可能性のあるフィールドを指定し、最適化コンパイラがフィールドの現在の値の永続性について推測することを防ぎます。
static修飾子
修飾子staticは、メンバーが特定のオブジェクトではなくクラスに属していることを示します。static とマークされたクラスには、static メンバーのみを含めることができます。static メンバーは、クラスのインスタンスではなくクラス全体に適用されるため、
クラス メンバーと呼ばれることもあります。
public class Foo { public static void Something () { ... } } // クラスメソッドを呼び出します。Foo . Something ( );
アクセス修飾子
アクセス修飾子、または継承修飾子 は、クラス、メソッド、およびその他のメンバーのアクセス可能性を設定します。 マークされたものは、publicどこからでもアクセスできます。privateメンバー は、宣言されているクラスの内部からのみアクセスでき、継承されると非表示になります。protected修飾子を持つメンバー は になりますprivateが、継承されるとアクセス可能になります。internalクラスと メンバー は、宣言アセンブリの内部からのみアクセス可能です。
クラスと構造体は暗黙的にアクセス修飾子がない場合は
internal暗黙的にメンバーになります。private
パブリッククラスFoo { public int Do () { return 0 ; }
パブリッククラスBar {
}
}
この表は、アクセス修飾子を使用できる場所を定義します。
コンストラクター
コンストラクターは、オブジェクトの作成時に自動的に呼び出される特別なメソッドです。その目的は、オブジェクトのメンバーを初期化することです。コンストラクターはクラスと同じ名前を持ち、明示的には何も返しません。new演算子を介して呼び出されると、暗黙的に新しく作成されたオブジェクトを返します。他のメソッドと同様に、パラメーターを受け取ることができます。パラメーターなしのコンストラクターは、ジェネリック型パラメーターの必要な制約として指定できるため、特別です。
クラスFoo { Foo () { ... } }
コンストラクタはpublic、、またはに
なりますprivate。protectedinternal
デストラクタ
デストラクタは、オブジェクトがガベージ コレクタによって収集され、手動クリーンアップを実行するときに呼び出されます。 と呼ばれるデフォルトのデストラクタ メソッドがありfinalize、これを宣言することでオーバーライドできます。
構文はコンストラクタの構文に似ています。違いは、名前の前に ~ が付き、パラメータを含めることができないことです。デストラクタは複数存在できません。
クラスFoo { ...
~ Foo ()
{ ... } }
ファイナライザは常に ですprivate。
方法
C や C++ と同様に、再利用可能なコードをグループ化する関数があります。主な違いは、関数は Java と同様に、クラス内に存在する必要があることです。したがって、関数は メソッドと呼ばれます。メソッドには、戻り値、名前、および通常はいくつかの引数で呼び出されたときに初期化されるいくつかのパラメーターがあります。クラスのインスタンスに属するか、静的メンバーになることができます。
クラスFoo { int Bar ( int a 、int b ) { return a % b ; } }
メソッドは、.特定の変数の表記法、または静的メソッドの場合は型の名前
を使用して呼び出されます。
Foo foo = new Foo (); int r = foo . Bar ( 7 , 2 );
コンソール.WriteLine ( r ) ;
refおよびoutパラメータ
refキーワードまたはで始まるパラメータを持つメソッドを呼び出すときに、引数を明示的に参照渡しすることができますout。これらのマネージド ポインタは、メソッド内で変更する変数を参照渡しする場合に便利です。 2 つの主な違いは、outメソッドが戻るまでに、メソッド内でパラメータが割り当てられている必要があることです。 はref新しい値を割り当てることも割り当てないこともできますが、関数を呼び出す前にパラメータ変数を初期化する必要があります。
void PassRef ( ref int x ) { if ( x == 2 ) x = 10 ; } int Z = 7 ; PassRef ( ref Z );
void PassOut ( out int x ) { x = 2 ; } int Q ; PassOut ( out Q );
オプションパラメータ
- これはC# 4.0の機能です。
C# 4.0 では、C++ と同様にデフォルト値を持つオプション パラメータが導入されています。例:
voidインクリメント( ref int x 、int dx = 1 ) { x += dx ; }
int x = 0 ; Increment ( ref x ); // dx はデフォルト値の 1 を取得します。Increment ( ref x , 2 ); // dx は値 2 を取得します。
さらに、オプション パラメータを補完するために、メソッド呼び出しでパラメータ名を明示的に指定して、メソッドのオプション パラメータの任意のサブセットを選択的に渡すことができます。唯一の制限は、名前付きパラメータは名前なしパラメータの後に配置する必要があることです。パラメータ名はオプション パラメータと必須パラメータの両方に指定でき、読みやすさを向上させたり、呼び出しで引数を任意に並べ替えたりするために使用できます。例:
ストリームOpenFile (文字列名、FileModeモード= FileMode.Open 、FileAccessアクセス= FileAccess.Read ) { ... }
OpenFile ( "file.txt" ); // "mode" と "access" の両方にデフォルト値を使用します。OpenFile ( "file.txt" 、mode : FileMode . Create ); // "access" にデフォルト値を使用します。OpenFile ( "file.txt" 、access : FileAccess . Read ); // "mode" にデフォルト値を使用します。OpenFile ( name : "file.txt" 、access : FileAccess . Read 、mode : FileMode . Create ); // 読みやすくするためにすべてのパラメータに名前を付け、// メソッド宣言とは異なる順序を使用します。
オプション パラメータを使用すると、COM との相互運用が容易になります。以前は、C# では、オプションのパラメータも含め、COM コンポーネントのメソッドのすべてのパラメータを渡す必要がありました。次に例を示します。
オブジェクトfileName = " Test.docx " ;オブジェクトmissing = System.Reflection.Missing.Value ;
doc . SaveAs ( ref fileName 、ref missing 、ref missing 、ref missing 、ref missing 、ref missing 、ref missing 、ref missing 、ref missing 、ref missing 、ref missing 、ref missing 、ref missing 、ref missing 、ref missing ) ; console . writeline ( "ファイルが正常に保存されました" ) ;
オプションパラメータのサポートにより、コードは次のように短縮できます。
doc . SaveAs ( refファイル名);
extern
C# の特徴の 1 つは、ネイティブ コードを呼び出す機能です。メソッド シグネチャは、本体なしで単純に宣言され、 としてマークされますextern。DllImport目的の DLL ファイルを参照するには、属性も追加する必要があります。
[DllImport("win32.dll")]
静的外部ダブルPow ( double a 、double b );
フィールド
フィールド、つまりインスタンス変数は、データを保存するためにクラス本体内で宣言できます。
クラスFoo { double foo ; }
フィールドは宣言時に直接初期化できます (構造体で宣言されていない場合)。
クラスFoo { double foo = 2.3 ; }
フィールドの修飾子:
const- フィールドを定数にします。private- フィールドをプライベートにします (デフォルト)。protected- フィールドを保護します。public- フィールドを公開します。readonly- フィールドをコンストラクター内で 1 回だけ初期化できるようにします。static- フィールドを静的メンバー、つまりクラス変数にします。
プロパティ
プロパティはフィールドのような構文を持ち、それをメソッドのパワーと組み合わせます。プロパティにはgetと の2 つのアクセサを設定できますset。
パブリッククラスPerson {プライベート文字列_name ;
文字列名{ get { return _name ; } set { _name = value ; } } }
// プロパティを使用する
var person = new Person (); person . Name = "Robert" ;
プロパティの修飾子:
private- プロパティをプライベートにします (デフォルト)。protected- プロパティを保護します。public- プロパティを公開します。static- プロパティを静的メンバーにします。
プロパティ アクセサーの修飾子:
private- アクセサーをプライベートにします。protected- アクセサを保護します。public- アクセサーを公開します。
アクセサのデフォルトの修飾子はプロパティから継承されます。アクセサの修飾子は、プロパティの修飾子と同じかそれ以上に制限されることに注意してください。
自動プロパティ
- これはC# 3.0の機能です。
C# 3.0 の機能の 1 つは、自動実装プロパティです。本体なしでアクセサーを定義すると、コンパイラーはバッキング フィールドとアクセサーに必要なコードを生成します。
パブリックdouble幅{取得;プライベート設定; }
インデクサー
インデクサーは、オブジェクトに配列のようなインデックス作成機能を追加します。プロパティと同様の方法で実装されます。
内部クラスIntList { private int [] _items ;
int this [ int index ] { get { return _items [ index ]; } set { _items [ index ] = value ; } } }
// インデクサーを使用する
var list = new IntList (); list [ 2 ] = 2 ;
継承
C# のクラスは 1 つのクラスからのみ継承できます。クラスは、 としてマークされていない任意のクラスから派生できますsealed。
クラスA { }
クラスB : A {
}
virtual
マークされたメソッドはvirtual実装を提供しますが、キーワードを使用して継承によってオーバーライドできますoverride。
実装は変数の型ではなく、オブジェクトの実際の型によって選択されます。
クラスOperation { public virtual int Do () { return 0 ; } }
クラスNewOperation : Operation { public override int Do () { return 1 ; } }
new
非仮想メソッドを別のシグネチャでオーバーロードする場合、キーワードnewを使用できます。使用されるメソッドは、オブジェクトの実際の型ではなく、変数の型によって選択されます。
クラスOperation { public int Do () { return 0 ; } }
クラスNewOperation : Operation { public new double Do () { return 4.0 ; } }
これはその事例を示しています:
var操作=新しいNewOperation ();
// NewOperation で "double Do()" を呼び出します
double d = operation . Do ();
操作operation_ =操作;
// Operation で "int Do()" を呼び出します
int i = operation_ . Do ();
abstract
抽象クラスはテンプレートとしてのみ機能するクラスであり、そのタイプのオブジェクトを初期化することはできません。それ以外は、通常のクラスと同じです。
抽象メンバーが存在する場合もあります。抽象メンバーは、実装を持たない抽象クラスのメンバーです。メンバーを継承する非抽象クラスによってオーバーライドされる必要があります。
抽象クラスMammal { public abstract void Walk (); }
クラスHuman : Mammal { public override void Walk () {
}
...
}
sealed
このsealed修飾子は、クラスを継承不可にするためのオプションの修飾子として、または派生クラスでのメソッドのオーバーライドを禁止するためのオプションの修飾子として、他の修飾子と組み合わせて使用できます。
内部シールクラスFoo { //... }
パブリッククラスBar {パブリック仮想void Action () { //... } }
パブリッククラスBaz : Bar {パブリックシールオーバーライドvoid Action () { //... } }
インターフェース
インターフェイスは、実際の実装ではなくメンバー定義を含むデータ構造です。異なる実装を持つ異なる型のメンバー間の契約を定義する場合に便利です。メソッド、プロパティ、およびインデクサーの定義を宣言できます。インターフェイス メンバーは暗黙的にパブリックです。インターフェイスは暗黙的にまたは明示的に実装できます。
インターフェースIBinaryOperation { double A { get ; set ; } double B { get ; set ; }
ダブルGetResult (); }
インターフェースの実装
インターフェースは、クラスによって実装されるか、:表記法を使用してクラスが別のクラスから派生されるのと同じ方法で、別のインターフェースによって拡張されます。
暗黙的な実装
インターフェースを暗黙的に実装する場合、インターフェースのメンバーは である必要がありますpublic。
パブリッククラスAdder : IBinaryOperation {パブリックdouble A {取得;設定; }パブリックdouble B {取得;設定; }
パブリックダブルGetResult (){戻り値A + B ; } }
パブリッククラスMultiplier : IBinaryOperation {パブリックdouble A {取得;設定; }パブリックdouble B {取得;設定; }
パブリックダブルGetResult (){戻り値A * B ; } }
使用中:
IBinaryOperation op = null ;結果はdouble ;
// Adder は IBinaryOperation インターフェイスを実装します。
op =新しい加算器();オプ。A = 2 ;オプ。B = 3 ;
結果=操作。GetResult (); // 5
// Multiplier もインターフェースを実装します。
op =新しい乗数();オプ。A = 5 ;オプ。B = 4 ;
結果=操作。GetResult (); // 20
明示的な実装
メンバーを明示的に実装することもできます。クラスによって明示的に実装されたインターフェースのメンバーは、オブジェクトがインターフェース型として処理される場合にのみアクセスできます。
パブリッククラスAdder : IBinaryOperation { double IBinaryOperation.A { get ; set ; } double IBinaryOperation.B { get ; set ; }
ダブルIBinaryOperation . GetResult () { return (( IBinaryOperation ) this ). A + (( IBinaryOperation ) this ). B ; } }
使用中:
加算器add = new Adder ();
// これらのメンバーにはアクセスできません:
// add.A = 2;
// add.B = 3;
// double result = add.GetResult();
// アクセスするにはインターフェース型にキャストします: IBinaryOperation add2 = add ; add2.A = 2 ; add2.B = 3 ;
ダブル結果= add2 . GetResult ();
注:拡張するクラスのプロパティはIBinaryOperationコンパイラによって自動的に実装され、バッキング フィールドが自動的に追加されます (「#自動プロパティ」を参照)。
複数のインターフェースの拡張
インターフェースとクラスは複数のインターフェースを拡張できます。
クラスMyClass : IInterfaceA 、IInterfaceB { ... }
以下は 2 つのインターフェースを拡張するインターフェースです。
インターフェースIInterfaceC : IInterfaceA 、IInterfaceB { ... }
インターフェースと抽象クラス
インターフェースと抽象クラスは似ています。次に、いくつかの重要な違いについて説明します。
- 抽象クラスには、非抽象メソッドやプロパティだけでなく、メンバー変数も含めることができます。インターフェースにはこれらを含めることはできません。
- クラスまたは抽象クラスは、 1 つのクラスまたは抽象クラスからのみ継承できます。
- クラスまたは抽象クラスは、1 つ以上のインターフェースを実装できます。
- インターフェースは他のインターフェースを拡張することしかできません。
- 抽象クラスには、非公開のメソッドとプロパティ (抽象のものも含む) を含めることができます。インターフェースには、公開メンバーのみを含めることができます。
- 抽象クラスには定数、静的メソッド、静的メンバーを含めることができます。インターフェースにはこれらを含めることはできません。
- 抽象クラスにはコンストラクターが存在する場合があります。インターフェースには存在しません。
ジェネリック
- これはC# 2.0および.NET Framework 2.0の機能です。
ジェネリック(またはパラメータ化された型、パラメトリック多態性)は型パラメータを使用するため、クラスやメソッドがインスタンス化されるまで型を指定しないクラスやメソッドを設計することができます。主な利点は、ジェネリック型パラメータを使用して、実行時のキャストやボックス化操作のコストをかけずに使用できるクラスやメソッドを作成できることです。以下を参照してください。[16]
// ジェネリッククラスを宣言します。
パブリッククラスGenericList < T > { void Add ( T input ) { } }
class TestGenericList { private class ExampleClass { } static void Main () { // int 型のリストを宣言します。var list1 = new GenericList < int > ();
// 文字列型のリストを宣言します。var
list2 = new GenericList < string > ( );
// ExampleClass 型のリストを宣言します。var list3 = new GenericList < ExampleClass > (); } }
C# ジェネリックはC++ テンプレートと比較すると安全性が強化されていますが、機能が多少制限されています。[17]たとえば、C# ジェネリック型では算術演算子を呼び出すことはできません。[18] C++ テンプレートとは異なり、.NET のパラメーター化された型はコンパイラではなく実行時にインスタンス化されます。そのため、C++ テンプレートでは言語間互換性がありませんが、.NET のパラメーター化された型は言語間互換性があります。インターフェイスの使用によるジェネリック パラメーターの型制約など、C++ テンプレートでは直接サポートされていない機能もサポートしています。一方、C# は非型ジェネリック パラメーターをサポートしていません。
Javaのジェネリックとは異なり、.NETジェネリックは具体化を使用してパラメータ化された型を共通言語基盤(CLI)仮想マシンのファーストクラスオブジェクトにします。これにより、型情報の最適化と保持が可能になります。[19]
ジェネリックの使用
ジェネリッククラス
クラスと構造体はジェネリックにすることができます。
パブリッククラスList < T > { ...パブリックvoid Add ( T item ) { ... } }
var list = new List < int > ( ) ; list.Add ( 6 ) ; list.Add ( 2 ) ;
汎用インターフェース
インターフェースIEnumerable < T > { ... }
ジェネリックデリゲート
委任R Func < T1 , T2 , R > ( T1 a1 , T2 a2 );
ジェネリックメソッド
パブリック静的T [] CombineArrays < T > ( T [] a 、T [] b ) { T [] newArray = new T [ a . Length + b . Length ]; a . CopyTo ( newArray 、0 ); b . CopyTo ( newArray 、a . Length ); return newArray ; }
文字列[] a =新しい文字列[] { "a" , "b" , "c" };文字列[] b =新しい文字列[] { "1" , "2" , "3" };文字列[] c = CombineArrays ( a , b );
double [] da =新しいdouble [] { 1.2 、2.17 、3.141592 }; double [] db =新しいdouble [] { 4.44 、5.6 、6.02 }; double [] dc = CombineArrays ( da 、db );
// c は { "a", "b", "c", "1", "2", "3"} を含む文字列配列です。//
dc は { 1.2, 2.17, 3.141592, 4.44, 5.6, 6.02} を含む double 配列です。
型パラメータ
型パラメータは、新しいジェネリックを定義するときに具体的な型の代わりに使用される名前です。型パラメータを山括弧で囲むことで、クラスまたはメソッドに関連付けることができます。ジェネリックをインスタンス化(または呼び出し)するときに、宣言で指定した型パラメータを具体的な型に置き換えることができます。型パラメータは、キーワードと制約仕様を使用して制約することができ、6つのコンマ区切りの制約のいずれかを使用できます。[20]< >where
共変性と反変性
- これはC# 4.0および.NET Framework 4.0の機能です。
ジェネリックインターフェイスとデリゲートは、それぞれキーワードと を使用して、型パラメータを共変または反変としてマークできます。これらの宣言は、暗黙的および明示的、コンパイル時および実行時の両方の型変換で尊重されます。たとえば、既存のインターフェイスは次のように再定義されています。
outinIEnumerable<T>
インターフェイスIEnumerable < out T > { IEnumerator < T > GetEnumerator (); }
したがって、あるクラスを実装するクラスは、直接的または間接的に拡張するすべてのクラスおよびインターフェースとも互換性があると見なされます。実際には、次のようなコードを記述できます。
IEnumerable<Derived>DerivedIEnumerable<Base>BaseDerived
void PrintAll ( IEnumerable <object> objects ) { foreach ( object o in objects ) { System.Console.WriteLine ( o ) ; } }
IEnumerable < string > strings = new List < string > (); PrintAll ( strings ); // IEnumerable<string> は暗黙的に IEnumerable<object> に変換されます
反変性のために、既存のインターフェースは次のように再定義されました。
IComparer<T>
パブリックインターフェイスIComparer < in T > { int Compare ( T x , T y ); }
したがって、あるクラスに対して を実装するクラスは、から拡張されたすべてのクラスおよびインターフェースに対してと互換性があると見なされます。これにより、次のようなコードを記述できるようになります。
IComparer<Base>BaseIComparer<Derived>DerivedBase
IComparer <オブジェクト> objectComparer = GetComparer (); IComparer <文字列> stringComparer = objectComparer ;
列挙子
列挙子は反復子です。列挙子は通常、インターフェイスを実装するオブジェクトの メソッドを呼び出すことによって取得されます。コンテナー クラスは通常、このインターフェイスを実装します。ただし、C#のforeachステートメントは、 を実装していなくても、そのようなメソッドを提供する任意のオブジェクトに対して操作できます。このインターフェイスは、.NET 2.0で汎用バージョンに拡張されました。
GetEnumerator()IEnumerableIEnumerable
以下は、C# 2.0 での反復子の簡単な使用法を示しています。
//明示的なバージョンIEnumerator < MyType > iter = list.GetEnumerator ( ) ; while ( iter.MoveNext ( ) ) Console.WriteLine ( iter.Current ) ;
//暗黙バージョンforeach ( MyType value in list ) Console.WriteLine ( value );
ジェネレータ機能
- これはC# 2.0の機能です。
.NET 2.0 Frameworkでは、Pythonの に似た構造を使用して、ジェネレーター機能を提供する反復子をC#に導入できました。[21]を使用すると、関数は反復中に自動的に状態を保持します。
yield returnyieldyield return
// 反復可能な入力(配列の場合もある)を受け取り、すべての偶数を返すメソッド。public
static IEnumerable < int > GetEven ( IEnumerable < int > numbers ) { foreach ( int i in numbers ) { if ( i % 2 == 0 ) yield return i ; } }
// メソッドを使用して配列から偶数のみを出力します
static void Main () { int [] numbers = { 1 , 2 , 3 , 4 , 5 , 6 }; foreach ( int i in GetEven ( numbers )) Console . WriteLine ( i ); // 2、4、6 を出力します}
リンク
- This is a feature of C# 3.0 and .NET Framework 3.0.
LINQ, short for Language Integrated Queries, is a .NET Framework feature which simplifies the handling of data. Mainly it adds support that allows the querying of arrays, collections, and databases. It also introduces binders, which makes it easier to access to databases and their data.
Query syntax
The LINQ query syntax was introduced in C# 3.0 and lets one write SQL-like queries in C#.
var list = new List<int>{ 2, 7, 1, 3, 9 };
var result = from i in list
where i > 1
select i;
The statements are compiled into method calls, whereby almost only the names of the methods are specified. Which methods are ultimately used is determined by normal overload resolution. Thus, the result of the translation is affected by what symbols are in scope.
What differs from SQL is that the from-statement comes first and not last as in SQL. This is because it seems more natural writing like this in C# [citation needed] and supports "Intellisense" (Code completion in the editor).
Anonymous methods
Anonymous methods, or in their present form more commonly referred to as "lambda expressions", is a feature which allows programmers to write inline closure-like functions in their code.
There are various ways to create anonymous methods. Prior to C# 3.0 there was limited support by using delegates.
Anonymous delegates
- This is a feature of C# 2.0.
Anonymous delegates are functions pointers that hold anonymous methods. The purpose is to make it simpler to use delegates by simplifying the process of assigning the function. Instead of declaring a separate method in code the programmer can use the syntax to write the code inline and the compiler will then generate an anonymous function for it.
Func<int, int> f = delegate(int x) { return x * 2; };
Lambda expressions
- This is a feature of C# 3.0.
Lambda expressions provide a simple syntax for inline functions that are similar to closures. Functions with parameters infer the type of the parameters if other is not explicitly specified.
// [arguments] => [method-body]
// With parameters
n => n == 2
(a, b) => a + b
(a, b) => { a++; return a + b; }
// With explicitly typed parameters
(int a, int b) => a + b
// No parameters
() => return 0
// Assigning lambda to delegate
Func<int, int, int> f = (a, b) => a + b;
Multi-statement lambdas have bodies enclosed by braces and inside of them code can be written like in standard methods.
(a, b) => { a++; return a + b; }
Lambda expressions can be passed as arguments directly in method calls similar to anonymous delegates but with a more aesthetic syntax.
var list = stringList.Where(n => n.Length > 2);
Lambda expressions are essentially compiler-generated methods that are passed via delegates. These methods are reserved for the compiler only and can not be used in any other context.
Extension methods
- This is a feature of C# 3.0.
Extension methods are a form of syntactic sugar providing the illusion of adding new methods to the existing class outside its definition. In practice, an extension method is a static method that is callable as if it were an instance method; the receiver of the call is bound to the first parameter of the method, decorated with keyword this:
public static class StringExtensions
{
public static string Left(this string s, int n)
{
return s.Substring(0, n);
}
}
string s = "foo";
s.Left(3); // same as StringExtensions.Left(s, 3);
Local functions
- This is a feature of C# 7.0.
Local functions can be defined in the body of another method, constructor or property's getter and setter. Such functions have access to all variables in the enclosing scope, including parent method local variables. They are in scope for the entire method, regardless of whether they're invoked before or after their declaration. Access modifiers (public, private, protected) cannot be used with local functions. Also they do not support function overloading. It means there cannot be two local functions in the same method with the same name even if the signatures don't overlap.[22] After a compilation, a local function is transformed into a private static method, but when defined it cannot be marked static.[23]
In code example below, the Sum method is a local function inside Main method. So it can be used only inside its parent method Main:
static void Main(string[] args)
{
int Sum(int x, int y)
{
return x + y;
}
Console.WriteLine(Sum(10, 20));
Console.ReadKey();
}
Miscellaneous
Closure blocks
C# implements closure blocks by means of the using statement. The using statement accepts an expression which results in an object implementing IDisposable, and the compiler generates code that guarantees the object's disposal when the scope of the using-statement is exited. The using statement is syntactic sugar. It makes the code more readable than the equivalent try ... finally block.
public void Foo()
{
using (var bar = File.Open("Foo.txt"))
{
// do some work
throw new Exception();
// bar will still get properly disposed.
}
}
Thread synchronization
C# provides the lock statement, which is yet another example of beneficial syntactic sugar. It works by marking a block of code as a critical section by mutual exclusion of access to a provided object. Like the using statement, it works by the compiler generating a try ... finally block in its place.
private static StreamWriter _writer;
public void ConcurrentMethod()
{
lock (_writer)
{
_writer.WriteLine("Line 1.");
_writer.WriteLine("Followed by line 2.");
}
}
Attributes
Attributes are entities of data that are stored as metadata in the compiled assembly. An attribute can be added to types and members like properties and methods. Attributes can be used for better maintenance of preprocessor directives.
[CompilerGenerated]
public class $AnonymousType$120
{
[CompilerGenerated]
public string Name { get; set; }
}
The .NET Framework comes with predefined attributes that can be used. Some of them serve an important role at runtime while some are just for syntactic decoration in code like CompilerGenerated. It does only mark that it is a compiler-generated element. Programmer-defined attributes can also be created.
An attribute is essentially a class which inherits from the System.Attribute class. By convention, attribute classes end with "Attribute" in their name. This will not be required when using it.
public class EdibleAttribute : Attribute
{
public EdibleAttribute() : base()
{
}
public EdibleAttribute(bool isNotPoisonous)
{
this.IsPoisonous = !isNotPoisonous;
}
public bool IsPoisonous { get; set; }
}
Showing the attribute in use using the optional constructor parameters.
[Edible(true)]
public class Peach : Fruit
{
// Members if any
}
Preprocessor
C# features "preprocessor directives"[24] (though it does not have an actual preprocessor) based on the C preprocessor that allow programmers to define symbols, but not macros. Conditionals such as #if, #endif, and #else are also provided.
Directives such as #region give hints to editors for code folding. The #region block must be terminated with a #endregion directive.
public class Foo
{
#region Constructors
public Foo() {}
public Foo(int firstParam) {}
#endregion
#region Methods
public void IntBar(int firstParam) {}
public void StrBar(string firstParam) {}
public void BoolBar(bool firstParam) {}
#endregion
}
Code comments
C# utilizes a double slash (//) to indicate the rest of the line is a comment.
public class Foo
{
// a comment
public static void Bar(int firstParam) {} // Also a comment
}
Multi-line comments can be indicated by a starting slash/asterisk (/*) and ending asterisk/forward slash (*/).
public class Foo
{
/* A multi-line
comment */
public static void Bar(int firstParam) {}
}
Comments do not nest. These are two single comments:
// Can put /* */ */ */ /* /*
/* Can put /* /* /* but it ends with */
Single-line comments beginning with three slashes are used for XML documentation. This, however, is a convention used by Visual Studio and is not part of the language definition:
/// <summary>
/// This class is very classy.
/// </summary>
XML documentation comments
C#'s documentation comments[25] are similar to Java's Javadoc, but based on XML. Two methods of documentation are currently supported by the C# compiler.
Single-line documentation comments, such as those commonly found in Visual Studio generated code, are indicated on a line beginning with ///.
public class Foo
{
/// <summary>A summary of the method.</summary>
/// <param name="firstParam">A description of the parameter.</param>
/// <remarks>Remarks about the method.</remarks>
public static void Bar(int firstParam) {}
}
Multi-line documentation comments, while defined in the version 1.0 language specification, were not supported until the .NET 1.1 release.[26] These comments are designated by a starting forward slash/asterisk/asterisk (/**) and ending asterisk/forward slash (*/).[27]
public class Foo
{
/** <summary>A summary of the method.</summary>
* <param name="firstParam">A description of the parameter.</param>
* <remarks>Remarks about the method.</remarks> */
public static void Bar(int firstParam) {}
}
There are some stringent criteria regarding white space and XML documentation when using the forward slash/asterisk/asterisk (/**) technique.
This code block:
/**
* <summary>
* A summary of the method.</summary>*/
produces a different XML comment than this code block:[27]
/**
* <summary>
A summary of the method.</summary>*/
Syntax for documentation comments and their XML markup is defined in a non-normative annex of the ECMA C# standard. The same standard also defines rules for processing of such comments, and their transformation to a plain XML document with precise rules for mapping of Common Language Infrastructure (CLI) identifiers to their related documentation elements. This allows any C# integrated development environment (IDE) or other development tool to find documentation for any symbol in the code in a certain well-defined way.
Async-await syntax
- This is a feature of C# 5.0 and .NET Framework 4.0.
As of .NET Framework 4 there is a task library that makes it easier to write parallel and multi-threaded applications through tasks.
C# 5.0 has native language support for asynchrony.
Consider this code that takes advantage of the task library directly:
public static class SomeAsyncCode
{
public static Task<XDocument> GetContentAsync()
{
var httpClient = new HttpClient();
return httpClient.GetStringAsync("https://www.contoso.com/").ContinueWith((task) => {
string responseBodyAsText = task.Result;
return XDocument.Parse(responseBodyAsText);
});
}
}
var t = SomeAsyncCode.GetContentAsync().ContinueWith((task) => {
var xmlDocument = task.Result;
});
t.Start();
Here is the same logic written in the async-await syntax:
public static class SomeAsyncCode
{
public static async Task<XDocument> GetContentAsync()
{
var httpClient = new HttpClient();
string responseBodyAsText = await httpClient.GetStringAsync("https://www.contoso.com/");
return XDocument.Parse(responseBodyAsText);
}
}
var xmlDocument = await SomeAsyncCode.GetContentAsync();
// The Task will be started on call with await.
Dialects
Spec#
Spec# is a dialect of C# that is developed in parallel with the standard implementation from Microsoft. It extends C# with specification language features and is a possible future feature to the C# language. It also adds syntax for the code contracts API that was introduced in .NET Framework 4.0. Spec# is being developed by Microsoft Research.
This sample shows two of the basic structures that are used when adding contracts to code.
static void Main(string![] args)
requires args.Length > 0
{
foreach (string arg in args)
{
}
}
!is used to make a reference type non-nullable, e.g. it is not possible to set the value to null. This in contrast of nullable types which allow value types to be set as null.requiresindicates a condition that must be followed in the code. In this case the length of args is not allowed to be zero or less.
Non-nullable types
Spec# extends C# with non-nullable types that simply checks so the variables of nullable types that has been set as non-nullable are not null. If is null then an exception will be thrown.
string! input
In use:
public Test(string! input)
{
...
}
Preconditions
Preconditions are checked before a method is executed.
public Test(int i)
requires i > 0;
{
this.i = i;
}
Postconditions
Postconditions are conditions that are ensured to be correct when a method has been executed.
public void Increment()
ensures i > 0;
{
i++;
}
Checked exceptions
Spec# adds checked exceptions like those in Java.
public void DoSomething()
throws SomeException; // SomeException : ICheckedException
{
...
}
Checked exceptions are problematic, because when a lower-level function adds a new exception type, the whole chain of methods using this method at some nested lower level must also change its contract. This violates the open/closed principle.[28]
See also
References
- ^ "C# Coding Conventions". Microsoft Learn. sec. Naming conventions. Archived from the original on January 16, 2023.
- ^ a b Wagner, Bill. "C# Keywords". docs.microsoft.com. Retrieved August 26, 2022.
- ^ Schildt, Herbert (December 30, 2008), C# 3.0: The Complete Reference, ISBN 9780071588416
- ^ Deitel, Harvey M.; Deitel, Paul J. (November 21, 2005), C# for programmers, ISBN 9780132465915
- ^ Wagner, Bill. "C# Keywords". docs.microsoft.com. Retrieved August 26, 2022.
- ^ a b Wagner, Bill. "Integral numeric types". learn.microsoft.com. Retrieved October 27, 2024.
- ^ Wagner, Bill. "Floating-point numeric types". learn.microsoft.com. Retrieved October 27, 2024.
- ^ Wagner, Bill. "char". learn.microsoft.com. Retrieved October 27, 2024.
- ^ a b Wagner, Bill. "Strings and string literals". learn.microsoft.com. Retrieved October 27, 2024.
- ^ a b Wagner, Bill. "Declaration statements". learn.microsoft.com. Retrieved October 27, 2024.
- ^ Wagner, Bill. "Assignment operators". learn.microsoft.com. Retrieved October 27, 2024.
- ^ Wagner, Bill. "readonly". learn.microsoft.com. Retrieved October 27, 2024.
- ^ "Exploring C# 10: Save Space with File-Scoped Namespaces". Dave Brock. October 5, 2021. Retrieved November 19, 2024.
- ^ Pointer types (C# Programming Guide)
- ^ a b Archer, Part 2, Chapter 4:The Type System
- ^ "Generics (C# Programming Guide)". Microsoft. Retrieved August 7, 2011.
- ^ "An Introduction to C# Generics". Microsoft.
- ^ "Differences Between C++ Templates and C# Generics". Microsoft.
- ^ "An Introduction to C# Generics". Microsoft. January 2005. Retrieved June 18, 2009.
- ^ Constraints on Type Parameters (C# Programming Guide) in Microsoft MSDN
- ^ "yield". C# Language Reference. Microsoft. Retrieved April 26, 2009.
- ^ ".NET Framework - What's New in C# 7.0". msdn.microsoft.com. Retrieved April 8, 2017.
- ^ "Thoughts on C# 7 Local Functions". Anton Sizikov. April 15, 2016. Retrieved April 8, 2017.
- ^ 「C# プリプロセッサ ディレクティブ」。C # 言語リファレンス。Microsoft。2009年6 月 18日閲覧。
- ^ 「ドキュメントコメント」。Microsoft Learn。2023年 6 月 15 日。
- ^ Horton, Anson (2006 年 9 月 11 日)。「C# XML ドキュメント コメント FAQ」。2007 年12 月 11 日閲覧。
- ^ ab 「ドキュメント タグの区切り文字」。C # プログラマーズ リファレンス。Microsoft。2008年 12 月 20 日時点のオリジナルよりアーカイブ。2009年6 月 18 日閲覧。
- ^ Martin, Robert C. ( 2008年 8 月 11 日)。「7 エラー処理、未チェック例外の使用」。Clean Code: A Handbook of Agile Software Craftsmanship。Prentice Hall International。ISBN 978-0132350884。
- Archer, Tom (2001)。Inside C#。Microsoft Press。ISBN 0-7356-1288-9。
- Spec# の Bart de Smet 2010 年 10 月 29 日アーカイブ、Wayback Machineより
外部リンク
- マイクロソフト .NET フレームワーク
- モノプロジェクト
