「C Sharp」の版間の差分

出典: フリー教科書『ウィキブックス(Wikibooks)』
削除された内容 追加された内容
→‎目次: C Sharp/インストール方法|インストール方法とそれぞれの実行方法
→‎var: C#には、C言語でいうところの「グローバル変数」は無い。開発元のマイクロソフトの公式ドキュメントでも、そう明言されています
14 行 14 行
=== var ===
=== var ===
変数の宣言の際、intやcharなどの具体的な型を指定しなくても、宣言時の代入で具体的な数値または文字列の代入といっしょにvar とだけ指定すれば、代入されるデータから型を自動的に判断してくれます。
変数の宣言の際、intやcharなどの具体的な型を指定しなくても、宣言時の代入で具体的な数値または文字列の代入といっしょにvar とだけ指定すれば、代入されるデータから型を自動的に判断してくれます。



:<syntaxhighlight lang="csharp">
:<syntaxhighlight lang="csharp">
104 行 103 行




C# で宣言されている変数は、原則、どこかのクラスに所属していなければならない。C++のような感覚でもしコード冒頭のどこの class宣言もない部分で変数宣言するとエラーになりコンパイルできない。

:<syntaxhighlight lang="csharp">
using System;

var a = 4; // 変数宣言がクラスに包まれていないのでエラーになる。

public class sample {
public static void Main(string[] args) {
Console.WriteLine(a);

}
}
</syntaxhighlight>
; 実行結果
:エラーになりコンパイル不可


この仕様は明らかにC#やその手本のJavaが採用した「カプセル化」などの概念に基づいており、本質的な仕様であろう。「カプセル化」の概念とは、プログラムの部品をむきだしで管理することはせず、「カプセル」と言われるグループ単位でモジュール的・パッケージ的に管理することで大規模開発を効率化しようという1990~2001年頃の流行概念である。

つまり、C#には、C言語でいうところの「グローバル変数」は無い。開発元のマイクロソフトの公式ドキュメントでも、そう明言されています[https://docs.microsoft.com/ja-jp/dotnet/csharp/fundamentals/object-oriented/ Microsoft Docs 日本語訳『C# のクラス、構造体、レコードの概要』2022/06/10] (2022年6月18日に確認)。

これはつまりC#では、すべての変数は、なんらかのクラスに属するメンバです。

もしC#でどこからもアクセスしたい変数を定義したい場合は(ただし同じソースコードファイル内)、単に public class でクラス宣言しておいて、必要に応じてクラス内の変数宣言で static 修飾子をつけて宣言すればいいだけである。

別ファイルからのアクセスについては、初心者レベルを越えるので、ここでの説明は省略する。


=== タプル ===
=== タプル ===

2022年6月18日 (土) 08:58時点における版

C#とは、マイクロソフトによって開発されたプログラミング言語です。主な特徴としては、Javaに類似した言語構文を採用していること、.NET Coreというマイクロソフトが策定した共通言語基盤で動作することです。

目次

※編集中

C# はGUIも作成できますが、本ページでは説明の初期のうちは、コンソールアプリ(DOSプロンプトのような画面にテキスト表示するプログラムのこと)のプログラムを解説したいと思います。

C++になくてC#にある機能は多々ありますが、特にC++との違いが大きい話題を挙げたいと思います。

var

変数の宣言の際、intやcharなどの具体的な型を指定しなくても、宣言時の代入で具体的な数値または文字列の代入といっしょにvar とだけ指定すれば、代入されるデータから型を自動的に判断してくれます。

using System;

public class sample {
  public static void Main(string[] args) {

    var a = 4;    
    Console.WriteLine(a); 
    Console.WriteLine( a.GetType() );
    
    var b = "hello";    
    Console.WriteLine(b); 
    Console.WriteLine( b.GetType() );
    
    var c = 1.52;    
    Console.WriteLine(c); 
    Console.WriteLine( c.GetType() );
    
    var d = "w";    
    Console.WriteLine(d); 
    Console.WriteLine( d.GetType() );

  }
}
実行結果
4
System.Int32
hello
System.String
1.52
System.Double
w
System.String


C#9 以降、下記のように簡略できます。

 var a = 4;    
    Console.WriteLine(a); 
    Console.WriteLine( a.GetType() );
    
    var b = "hello";    
    Console.WriteLine(b); 
    Console.WriteLine( b.GetType() );
    
    var c = 1.52;    
    Console.WriteLine(c); 
    Console.WriteLine( c.GetType() );
    
    var d = "w";    
    Console.WriteLine(d); 
    Console.WriteLine( d.GetType() );


どちらにせよ、右辺値のない状態でvarキーワードを使うとエラーになり、コンパイルできません。varキーワードは右辺値から型を推論するキーワードなので、その右辺値そのものが無いと推論できないからです。

ほか、あるオブジェクトの型を取得するには、そのオブジェクトから .GetType() メソッドを使います。あるオブジェクトを指定してメソッドを使うには、オブジェクト名.メソッド名()の表記で実行します。

似たようなメソッドとして typeof() というのがありますが、これはクラスに対して用いるものと、記法が異なるので、このセクションでのtypeofの説明は省略します。


なお、varを使わずとも、下記のようにC言語と同様の従来の記法で変数宣言することもできます。

using System;

public class sample {
  public static void Main(string[] args) {

    int a  = 17;
    Console.WriteLine(a); 
    Console.WriteLine( a.GetType() );

  }
}
実行結果
17
System.Int32

また、この例から分かるように int は System.Int32 と同じです。


C# で宣言されている変数は、原則、どこかのクラスに所属していなければならない。C++のような感覚でもしコード冒頭のどこの class宣言もない部分で変数宣言するとエラーになりコンパイルできない。

using System;

var a = 4; // 変数宣言がクラスに包まれていないのでエラーになる。   

public class sample {
  public static void Main(string[] args) {
 
    Console.WriteLine(a); 

  }
}
実行結果
エラーになりコンパイル不可


この仕様は明らかにC#やその手本のJavaが採用した「カプセル化」などの概念に基づいており、本質的な仕様であろう。「カプセル化」の概念とは、プログラムの部品をむきだしで管理することはせず、「カプセル」と言われるグループ単位でモジュール的・パッケージ的に管理することで大規模開発を効率化しようという1990~2001年頃の流行概念である。

つまり、C#には、C言語でいうところの「グローバル変数」は無い。開発元のマイクロソフトの公式ドキュメントでも、そう明言されていますMicrosoft Docs 日本語訳『C# のクラス、構造体、レコードの概要』2022/06/10 (2022年6月18日に確認)。

これはつまりC#では、すべての変数は、なんらかのクラスに属するメンバです。

もしC#でどこからもアクセスしたい変数を定義したい場合は(ただし同じソースコードファイル内)、単に public class でクラス宣言しておいて、必要に応じてクラス内の変数宣言で static 修飾子をつけて宣言すればいいだけである。

別ファイルからのアクセスについては、初心者レベルを越えるので、ここでの説明は省略する。

タプル

複数の変数を宣言する際、タプル taple と言うものを使って、( ) で、まとめて宣言できます。

配列やクラスなどとの違いは、単に配列名やクラス名などに相当する名前が無くても、宣言できるぐらいです。

つまり、データの集まりを無名で定義できます。

using System;

public class sample {
  public static void Main(string[] args) {

	(int a, int b) =(15, 2);

	Console.WriteLine(a);
	Console.WriteLine(b); 
	
	b=5;
	
	Console.WriteLine(a);
	Console.WriteLine(b); 
	
  }
}
実行結果
15
2
15
5

タプルは単に変数の宣言をひとまとめに行うだけのものなので、宣言したあとは、それぞれの変数は、普通の変数と同様に上書きしたり計算したりできます。

このため、タプルを使わなくても、ひとつひとつ変数を宣言しても、同じ結果の処理が同じくらいのソースコード量で可能です。


データ型の異なる要素どうしも、下記のようにタプルでひとまとめに出来ます。実質的に、タプルは無名クラスのようなものでしょう。


using System;

public class sample {
  public static void Main(string[] args) {

	(int a, string g) =(15, "hello");

	Console.WriteLine(a);
	Console.WriteLine(g); 
		
  }
}
実行結果
15
hello


タプルは無名で使うためのものですが、名前をつけても構いません。タプルに名前をつけた場合、個々の項目の要素へのアクセスには、下記のように Item1 , Item2 などのプロパティを使います。

using System;

public class sample {
  public static void Main(string[] args) {

	(int a, string g) =(15, "hello");
        var k = (a, g) ; // 変数名は k
        
	Console.WriteLine(k.Item1);
	Console.WriteLine(k.Item2); 
		
  }
}
実行結果
15
hello


タプルの項目に名前をつけることも出来ます。こうすれば、Itemで何番目だったかを覚える必要はないし、もしタプル内の順番の入れ替えをしてもコードの修正を減らせます。

using System;

public class sample {
  public static void Main(string[] args) {

    var k = (year : 18, msg : "good morning" );
        
	Console.WriteLine(k.year);
	Console.WriteLine(k.msg); 
		
  }
}
実行結果
18
good morning

foraech

マイクロソフトの公式サイトにもありますが、たとえば foreach という機能が、C++ ではなかなか入門レベルでは見かけず独特です。(C++にもC++11以降は似たような機能はあるが、記法がだいぶ違う。)

以下、マイクロソフトmsdnのコード例『配列での foreach の使用 (C# プログラミング ガイド)』2022/04/06(2022年6月に確認)を参考にしたものです。

using System;

public class Hello {
  public static void Main(string[] args) {

    int[] numbers = { 4, 5, 6, 1, 2, 3, -2, -1, 0 };
    
    foreach (int i in numbers) {
    	System.Console.Write("{0} ", i);
    }
    
    System.Console.Write("\n");
    
  }
}
実行結果
4 5 6 1 2 3 -2 -1 0

foreach は、配列などのグループ的なまとまりをもったデータ構造をfor的に巡回させたいとき、いちいちループ条件文にて配列の要素数を指定しなくても、上述のようにforeach文中で in 配列名を指定すれば、それをもとに自動的に第0項から最後の項まで巡回するようにループ条件を設定してくれる機能です。

正確な言い回しなどは、MSDN 公式サイトを見てください。

書式指定

C# では、表示位置の指定には、引用符中での波カッコ { } を使います。なお、一般に、引用符中で表示位置指定のために使われる波カッコ { } のことを「プレースホルダー」と言います。

C++ の%d などと違い、C#では引数の順番どおりに書く必要はないです。ただし、たとえば3番目の引数なら {2} のように、波カッコ内の数値で指定する必要があります。

c# の数値の16進数(hexadecimal)出力の書式指定は下記のようにセミコロン: を使います。

ただし、2進数や8進数はこの方法には対応しておらず、代わりに Convert.ToString()関数を使って変換できます。

using System;

public class Hello {
  public static void Main(string[] args) {

    Console.WriteLine("{0} + {2} = {1}", 3, 3 + 5, 5) ; 
    
    // 十進数の11を変換するコード
    Console.WriteLine("{0:x} ", 11) ; // 16進数に変換
    // Console.WriteLine("{0:b} ", 11) ; // エラーになる
    Console.WriteLine(Convert.ToString(11, 2)); // 2進数に変換
    
  }
}
実行結果
3 + 5 = 8
b 
1011


特に書式を宣言しない場合、数値は10進数として扱われます。

ですが、10進数(decimal)の出力を明示したい場合、 {0:D} のように「:D」をつけることで可能です。

using System;

public class Hello {
  public static void Main(string[] args) {
    
    Console.WriteLine("{0:d} ", 13) ; // 10進数
    Console.WriteLine("{0:g} ", 13) ; // general
  }
}
実行結果
13
13

数値以外も対応可能な形式として、特に指定のない事を明示する場合、 {0:g} のように「:g」をつける方法もあります。一般 general の g です。

条件分岐

単なるswitch文

まず、switch文やenumはC#では下記のように使う。.net core 6 で下記コードが動くので、C#10 に対応しているはず。

switch文についての説明は省略する。2022年6月現在、まだ当wikiにC#のif文やswitch文の教材は無いので、標準Cなどの解説を参照してもらいたい。

week weekinst = week.Saturday;

switch (weekinst) {

case week.Saturday:
    Console.WriteLine("土曜日");
    break;
case week.Sunday:
    Console.WriteLine("日曜日");
    break;

default:
    Console.WriteLine("平日");
    break;
}

enum week { 
  Sunday, 
  Monday, 
  Tuesday, 
  Wednesday, 
  Thursday, 
  Friday, 
  Saturday 
}
実行結果
土曜日

switch式

C#7~8 から「switch 式」というのが追加された。下記のような処理ができる。

week weekval = week.Saturday;

string message = weekval switch  {        // switch式用のインスタンス的なものを作成しないといけない

  week.Saturday => "土曜日" ,

  week.Sunday => "日曜日" ,

  _  => "平日" , // default に相当

};

Console.WriteLine(message);


enum week { 
  Sunday, 
  Monday, 
  Tuesday, 
  Wednesday, 
  Thursday, 
  Friday, 
  Saturday 
}
実行結果
土曜日

switch式では条件に「default」は使えない。アンダーバー_で指定した条件以外の値の場合の結果を指定する。

C#のコンパイラでは、switch式の全ての値を網羅してないかぎり、アンダーバー_で指定しないと、警告が出る。よって上記のようなコードになる。

なお、

week.Saturday  =>Console.WriteLine("土曜日"); // エラー

と書いても、うまく行かない。コンパイラにvoid型に変換できないと出るが、しかし

void message = weekval switch  { // エラー   

などと書き換えてもエラーになる。

よって、上記コードのように、switch式の結果(右側)では、命令ではなく値だけを指定して事前に用意された変数に代入することになる。別途、その結果を代入した変数をつかって目的の処理をする命令が必要である。

record型

C#9からrecord型が追加された。書き換えを禁じるデータ構造である。

2022年6月現在、マイクロソフト Docs 公式サイトにあるコードでは、なぜかエラーになる『C# 9.0 の新機能 - C# ガイド | Microsoft Docs』。下記のように修正すると、なぜか成功する。

Person person = new("Nancy", "Davolio");
    Console.WriteLine(person);

record Person(string FirstName, string LastName);
実行結果
Person { FirstName = Nancy, LastName = Davolio }

「Person」がいきなり定義されているのが妙に感じるだろうが、しかしrecordの行をpersonの行より前に移動するとエラーになる。

なお、「Person」は別にキーワードではないので、たとえば「Hito」(人)とかに変えてもコンパイル成功する。

念のため実行してみれば、

Hito hito = new("Tom", "Davolio");
    Console.WriteLine(hito);

record Hito(string FirstName, string LastName);
実行結果
Hito { FirstName = Tom, LastName = Davolio }

となる。

WindowsでもLinuxでも同様の結果になる。

なお、確認に用いた .net sdk のバージョンはWindowsでは 6.0.301 であり、Fedora では 6.0.105 である。


レコードの各要素にアクセスするにはレコード名.要素名でアクセスできる。しかし書き換えは出来ない。書き換えさえしなければ、閲覧などは出来る。

Hito hito = new("Tom", "Davolio");
    Console.WriteLine(hito);

// hito.FirstName = "John"; // レコードを書き換えようとするとエラーになる。
Console.WriteLine(hito.FirstName );

record Hito(string FirstName, string LastName);

// Console.WriteLine(hito.LastName ); // ここに書くとエラーになる
実行結果
Hito { FirstName = Tom, LastName = Davolio }
Tom


record型の実体は書き換えできませんが、現物を残したうえで、コピーをして部分的に書き換えることはwithを使うことで可能です。


Person person01 = new ("Nancy", "Davolio");
Console.WriteLine(person01);

var person02 = person01 with{FirstName = "John"};

Console.WriteLine(person02);

Console.WriteLine(person01);

record Person(string FirstName, string LastName);
実行結果
Person { FirstName = Nancy, LastName = Davolio }
Person { FirstName = John, LastName = Davolio }
Person { FirstName = Nancy, LastName = Davolio }

実行結果の2行目と3行目を見比べれば分かるように、2行目の結果でwith書き換えをした派生変数 person02 を作成しても、もとのrecord型変数 person01 はそのままです。

メソッドとクラス

C# の「メソッド」と「クラス」の概念はお互いに関連しあっており、やや難しい。メソッドを読んでて分からなければ、とりあえずクラスも読んでみるのが良い。

メソッド

C言語でいう「関数」に似た機能として、c#では「メソッド」がある。

関数との違いとして、メソッドは必ずどこかのクラスまたは構造体に所属していなければならない。つまり、必ずどこかのクラスまたは構造体のブロック中にてメソッドは宣言されることになる。どこのクラスにも属してない むきだしのメソッドを宣言しても、それがコード中に存在しているだけでコンパイルできずにエラーになる。

うごくコード例
using System;

// ユーザー定義側のメソッドには public はなくてもいい
class testclass{
  static public void testfunc (){
    var a = 55;
    Console.WriteLine(a);
  }
}

public class sample {
  public static void Main(string[] args) {

    testclass.testfunc();
    
  }
}
実行結果
55


同じクラスの中にある関数を呼び出す場合なら、呼出時のクラス指定を省略できる。

using System;

public class sample {
  public static void Main(string[] args) {

    testfunc();
    
  }
  
  static public void testfunc (){
    var a = 14;
    Console.WriteLine(a);
  }
  
}
実行結果
14


クラス

C++でいうクラスは、データの集合をあらわすものの事である標準C言語とC++でいう「構造体」も、若干の違いはあるがクラスと似たようなものである。

だがC#は理念として、あらゆるものをクラスまたは構造体で管理するという方針があるので、C++的なクラスの性質だけでなく、C#特有の独特の性質がある。

基本

クラスの基本的な使いかたは下記のとおり。

コード例

using System;

class testclass {
  public string name;
  public int price;
}

public class sample {
  public static void Main(string[] args) {

    testclass a = new testclass() ;

    a.name = "牛乳";
    a.price = 140;
    Console.WriteLine(a.name);
    Console.WriteLine(a.price);
    
  }
}
実行結果
牛乳
140

上記コードでいう string name;int price; などをメンバという。C言語の構造体やC++の構造体/クラスでも同様の働きをするものを「メンバ」と言う。C#の用語はC++を踏襲したものである。

メンバは public にアクセス修飾子を指定しないかぎり、基本的には直接はアクセスできない。

コンストラクタ

下記コードのように、構造体またはクラス中に、構造体名あるいはクラス名と同じメソッド名をもつメソッドを書くと、その構造体/クラスを呼び出したときの処理を指定できる。

コード例

using System;

struct teststr {
  
  public string name;
  public int price;
  
  public teststr(string a, int b){
   name = a;
   price = b;
  }
}

public class sample {
  public static void Main(string[] args) {

    teststr milk = new teststr("牛乳", 150);
    Console.WriteLine(milk.name);
    Console.WriteLine(milk.price);
    Console.WriteLine();
    
    teststr cmilk = new teststr("コーヒー牛乳", 180);
    Console.WriteLine(cmilk.name);
    Console.WriteLine(cmilk.price);
    Console.WriteLine();
    
    Console.WriteLine(milk.name);
    Console.WriteLine(milk.price);
    Console.WriteLine();
    
  }
}
実行結果
牛乳
150

コーヒー牛乳
180

牛乳
150

クラスと構造体の違い

論より証拠、実際に構造体とクラスのコードをそれぞれ実行して結果を比べてみましょう。

まず、milk.price = 150;のようにメンバに直接に代入する場合は、クラスでも構造体でも結果は下記のように同じです。

コード例

using System;

struct teststr {
  
  public string name;
  public int price;
}

public class sample {
  public static void Main(string[] args) {

    teststr milk = new teststr();
    milk.name = "牛乳";
    milk.price = 150;

    teststr cmilk = new teststr();
    cmilk.name = "コーヒー牛乳";
    cmilk.price = 180;

    Console.WriteLine(cmilk.name);
    Console.WriteLine(cmilk.price);
    
  }
}
実行結果
コーヒー牛乳
180


コード例

using System;

class testclass {
  
  public string name;
  public int price;
}

public class sample {
  public static void Main(string[] args) {

    testclass milk = new testclass();
    milk.name = "牛乳";
    milk.price = 150;

    testclass cmilk = new testclass();
    cmilk.name = "コーヒー牛乳";
    cmilk.price = 180;

    Console.WriteLine(cmilk.name);
    Console.WriteLine(cmilk.price);
    
  }
}
実行結果
コーヒー牛乳
180

では違いはというと、・・・※調査中