「C Sharp」の版間の差分
→目次: C Sharp/変数 |
クラスをサブページ化したので、こちらメインページからは除去。 |
||
8 行 | 8 行 | ||
* [[C Sharp/変数]] |
* [[C Sharp/変数]] |
||
* [[C Sharp/クラスとメソッド]] |
* [[C Sharp/クラスとメソッド]] |
||
* C Sharp/条件分岐 |
|||
* C Sharp/ループや繰り返し |
|||
== ※編集中 == |
== ※編集中 == |
||
19 行 | 21 行 | ||
C# は現在では国際規格化されており、マイクロソフトの仕様とは独自に国際規格が存在しています。 |
C# は現在では国際規格化されており、マイクロソフトの仕様とは独自に国際規格が存在しています。 |
||
=== 変数の宣言 === |
|||
==== 型推論 var ==== |
|||
変数の宣言の際、intやcharなどの具体的な型を指定しなくても、宣言時の代入で具体的な数値または文字列の代入といっしょにvar とだけ指定すれば、代入されるデータから型を自動的に判断してくれます。なおvarとは「変数」を意味する英語 variable (ヴァリアブル)の略です。 |
|||
:<syntaxhighlight lang="csharp"> |
|||
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() ); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
; 実行結果 :<syntaxhighlight lang="text"> |
|||
4 |
|||
System.Int32 |
|||
hello |
|||
System.String |
|||
1.52 |
|||
System.Double |
|||
w |
|||
System.String |
|||
</syntaxhighlight> |
|||
C#9 以降、下記のように簡略できます。 |
|||
:<syntaxhighlight lang="csharp"> |
|||
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() ); |
|||
</syntaxhighlight> |
|||
どちらにせよ、右辺値のない状態でvarキーワードを使うとエラーになり、コンパイルできません。varキーワードは右辺値から型を推論するキーワードなので、その右辺値そのものが無いと推論できないからです。 |
|||
ほか、あるオブジェクトの型を取得するには、そのオブジェクトから .GetType() メソッドを使います。あるオブジェクトを指定してメソッドを使うには、<code>オブジェクト名.メソッド名()</code>の表記で実行します。 |
|||
似たようなメソッドとして typeof() というのがありますが、これはクラスに対して用いるものと、記法が異なるので、このセクションでのtypeofの説明は省略します。 |
|||
var は型を右辺から推論してくれますが、しかし、いちどある変数の型が指定されたら、C#では、その変数の型は変更できません。 |
|||
==== 型を指定した宣言 ==== |
|||
varを使わずとも、下記のようにC言語と同様の従来の記法で変数宣言することもできます。 |
|||
:<syntaxhighlight lang="csharp"> |
|||
using System; |
|||
public class sample { |
|||
public static void Main(string[] args) { |
|||
int a = 17; |
|||
Console.WriteLine(a); |
|||
Console.WriteLine( a.GetType() ); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
; 実行結果 :<syntaxhighlight lang="text"> |
|||
17 |
|||
System.Int32 |
|||
</syntaxhighlight> |
|||
また、この例から分かるように int は System.Int32 と同じです。 |
|||
なお、 int とは、「整数」を意味する英語 integer インテジャーの略です。 |
|||
C# では、変数の型は、宣言後は(型を)変更できません。他のプログラミング言語だと、宣言時に数値を代入した変数にあとで文字列を代入できしまうのもあるが、C#ではそういうのは不可能です。 |
|||
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 修飾子をつけて宣言すればいいだけである。 |
|||
:<syntaxhighlight lang="csharp"> |
|||
using System; |
|||
// このクラス testval がどこからでもアクセスできて書き換え可能 |
|||
public class testval{ |
|||
public static int a = 3; |
|||
public static int b =15; |
|||
} |
|||
public class sample { |
|||
public static void Main(string[] args) { |
|||
Console.WriteLine(testval.a); |
|||
testval.a = 203; |
|||
testval.b = 215; |
|||
Console.WriteLine(testval.a); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
; 実行結果 :<syntaxhighlight lang="text"> |
|||
3 |
|||
203 |
|||
</syntaxhighlight> |
|||
上記のような、どこからでもアクセスできるstatic変数をどう使うかについては、初心者には高度なので、クラスの節で別途に説明する。 |
|||
別ファイルからのアクセスについては、初心者レベルを越えるので、当面の間、説明は省略する。 |
|||
また、クラス側では変数宣言 var は不要です。新しく定義したクラス内で var で型推論をしようとするとエラーになります。 |
|||
下記のように、「ローカル変数でないものをvarで宣言するな」(意訳)とコンパイラに叱られます。 |
|||
<pre> |
|||
test.cs(5,19): error CS0825: The contextual keyword `var' may only appear within a local variable declaration |
|||
</pre> |
|||
このように、varには、型推論のほかにも、色々な意味があります。ローカル変数とはメソッド内(C言語でいう「関数」内)で定義されている変数のことです。 |
|||
つまり、C++でいうところの、なんらかの「関数」内でないと(C#でいう「メソッド」内でないと)、varは使えません。 |
|||
よく分からなければ、どこからでも出来るグローバル変数的なものをC#で作りたい場合、初心者のうちは、Main 関数内で変数宣言すれば、基本的に同じコードファイル中のどこからでもアクセスできるはずです。 |
|||
グローバル変数的な管理用の public class などを別途に作るのは、慣れてきてからで十分です。 |
|||
C++などと比べて一見すると面倒そうですし実際に初心者にとっての障壁のひとつですが、C#ではグローバル変数的にどこからでもアクセスできる変数を必要に応じてクラスでグループ化できるという、C++や標準Cにはない利点もあります。 |
|||
そもそもクラスやその元ネタの構造体は、いくつもある要素のグループ化のためのものです。 |
|||
==== 定数の宣言 ==== |
|||
書き換え不能な変数を「定数」と言います。 |
|||
値が変化しないのに「変数」というのも妙ですが、歴史的にそう言うのと、他によい言い回しがないので、そういうものだと割り切ってください。 |
|||
C#で変数を宣言するには const キーワードを使います。(C++ や 標準C と同じです。) |
|||
var と const は併用できません。 |
|||
<code>const int a</code> のように型指定(int の部分)といっしょに const を宣言することで使えます。 |
|||
:<syntaxhighlight lang="csharp"> |
|||
using System; |
|||
public class sample { |
|||
public static void Main(string[] args) { |
|||
const int a = 4; |
|||
Console.WriteLine(a); |
|||
// a = 7; // const変数を書き換えようとしているので、コメントアウトしないとエラー |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
; 実行結果 :<syntaxhighlight lang="text"> |
|||
4 |
|||
</syntaxhighlight> |
|||
もし、上記コードから<code>const</code>のキーワード部分を消せば、コメントアウトした<code> a = 7; </code> をコメントアウトしなくても実行できるようになります。読者はそれぞれ試してください(ほぼ同じ内容の繰り返しになるので、説明は省略)。 |
|||
=== 書式指定 === |
=== 書式指定 === |
||
609 行 | 394 行 | ||
実行結果の2行目と3行目を見比べれば分かるように、2行目の結果でwith書き換えをした派生変数 person02 を作成しても、もとのrecord型変数 person01 はそのままです。 |
実行結果の2行目と3行目を見比べれば分かるように、2行目の結果でwith書き換えをした派生変数 person02 を作成しても、もとのrecord型変数 person01 はそのままです。 |
||
== メソッドとクラス == |
|||
C# の「メソッド」と「クラス」の概念はお互いに関連しあっており、やや難しい。メソッドを読んでて分からなければ、とりあえずクラスも読んでみるのが良い。 |
|||
=== メソッド === |
|||
C言語でいう「関数」に似た機能として、c#では「メソッド」がある。 |
|||
関数との違いとして、メソッドは必ずどこかのクラスまたは構造体に所属していなければならない。開発元のマイクロソフトの公式ドキュメントでも、そう明言されています[https://docs.microsoft.com/ja-jp/dotnet/csharp/fundamentals/object-oriented/ Microsoft Docs 日本語訳『C# のクラス、構造体、レコードの概要』2022/06/10] (2022年6月18日に確認)。 |
|||
つまり、必ずどこかのクラスまたは構造体のブロック中にてメソッドは宣言されることになる。どこのクラスにも属してない むきだしのメソッドを宣言しても、それがコード中に存在しているだけでコンパイルできずにエラーになる。 |
|||
:うごくコード例<syntaxhighlight lang="csharp"> |
|||
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(); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
; 実行結果 :<syntaxhighlight lang="text"> |
|||
55 |
|||
</syntaxhighlight> |
|||
同じクラスの中にある関数を呼び出す場合なら、呼出時のクラス指定を省略できる。 |
|||
:<syntaxhighlight lang="csharp"> |
|||
using System; |
|||
public class sample { |
|||
public static void Main(string[] args) { |
|||
testfunc(); |
|||
} |
|||
static public void testfunc (){ |
|||
var a = 14; |
|||
Console.WriteLine(a); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
; 実行結果 :<syntaxhighlight lang="text"> |
|||
14 |
|||
</syntaxhighlight> |
|||
なお、変数宣言で用いる var は、メソッド内でのローカル変数の宣言です。 |
|||
ただし、Mainメソッドのブロック内のvarだけ、Mainメソッドよりも上位のメソッドは無いので結果的にMainメソッド内のvar宣言された変数があたかもグローバル変数のような働きをしますが、あくまで結果論です。 |
|||
さて、C#のカプセル化などの理念により、すべての変数は、なんらかのクラスに所属していなければなりません。 |
|||
変数宣言で static 修飾子をつけると、どこからでもアクセスできるし、どこでアクセスして読み書きしても結果は共有されます。このため、static修飾をつけることで、標準C や C++ でいうグローバル変数のように使えます。 |
|||
下記コードでは、Mainメソッド以外のユーザ定義メソッドから書き換えをできるかのテストをしています。 |
|||
:うごくコード例<syntaxhighlight lang="csharp"> |
|||
using System; |
|||
// 変数管理用 |
|||
public class testval{ |
|||
public static int a = 3; |
|||
public static int b =15; // 使わないけど比較用 |
|||
} |
|||
// ユーザ定義メソッド |
|||
class testclass{ |
|||
static public void testfunc (){ |
|||
// 書き換えテスト |
|||
testval.a = 403; |
|||
testval.b = 415; |
|||
Console.WriteLine("ユーザー定義メソッドにて書き換え実行"); |
|||
} |
|||
} |
|||
// Mainメソッド |
|||
public class sample { |
|||
public static void Main(string[] args) { |
|||
Console.WriteLine("aは"); |
|||
Console.WriteLine(testval.a); |
|||
//test inst = new test(); |
|||
Console.WriteLine("Main側での書き換えテスト"); |
|||
testval.a = 203; |
|||
//testval.b = 215; |
|||
Console.WriteLine(testval.a); |
|||
Console.WriteLine("これからユーザー定義メソッドでさらに書き換えを試みる"); |
|||
testclass.testfunc(); |
|||
Console.WriteLine("今、Mainに戻ったあとです"); |
|||
Console.WriteLine("aは"); |
|||
Console.WriteLine(testval.a); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
; 実行結果 :<syntaxhighlight lang="text"> |
|||
aは |
|||
3 |
|||
Main関数側での書き換えテスト |
|||
203 |
|||
これからユーザー定義関数でさらに書き換えを試みる |
|||
ユーザー定義関数にて書き換え実行 |
|||
今、Main関数に戻ったあとです |
|||
aは |
|||
403 |
|||
</syntaxhighlight> |
|||
このように、staticで宣言された変数は、書き換えしたメソッドとは他のメソッドに移っても、書き換えの結果が保存されています。 |
|||
「でもMainメソッドは、ユーザ定義メソッドの呼出元じゃないか? 呼出元以外ではどうなるんだ?」と疑問に思うなら、どうぞ試してください。結果は、Main以外の場所で結果確認をしても、上記コードと同様に書き換え結果は保存されます。 |
|||
:うごくコード例<syntaxhighlight lang="csharp"> |
|||
using System; |
|||
// 変数管理用 |
|||
public class testval{ |
|||
public static int a = 3; |
|||
public static int b =15; // 使わないけど比較用 |
|||
} |
|||
// ユーザ定義メソッド |
|||
class testclass{ |
|||
static public void testfunc (){ |
|||
// 書き換えテスト |
|||
testval.a = 403; |
|||
testval.b = 415; |
|||
Console.WriteLine("ユーザー定義メソッドにて書き換え実行"); |
|||
} |
|||
} |
|||
// 結果確認用メソッド |
|||
class testcheck{ |
|||
static public void testfunc2 (){ |
|||
Console.WriteLine("aは"); |
|||
Console.WriteLine(testval.a); |
|||
} |
|||
} |
|||
// Mainメソッド |
|||
public class sample { |
|||
public static void Main(string[] args) { |
|||
Console.WriteLine("aは"); |
|||
Console.WriteLine(testval.a); |
|||
//test inst = new test(); |
|||
Console.WriteLine("Main側での書き換えテスト"); |
|||
testval.a = 203; |
|||
//testval.b = 215; |
|||
Console.WriteLine(testval.a); |
|||
Console.WriteLine("これからユーザー定義メソッドでさらに書き換えを試みる"); |
|||
testclass.testfunc(); |
|||
Console.WriteLine("今、Mainに戻ったあとです"); |
|||
Console.WriteLine("結果チェック用メソッドに移動します"); |
|||
testcheck.testfunc2(); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
; 実行結果 :<syntaxhighlight lang="text"> |
|||
aは |
|||
3 |
|||
Main側での書き換えテスト |
|||
203 |
|||
これからユーザー定義メソッドでさらに書き換えを試みる |
|||
ユーザー定義メソッドにて書き換え実行 |
|||
今、Mainに戻ったあとです |
|||
結果チェック用メソッドに移動します |
|||
aは |
|||
403 |
|||
</syntaxhighlight> |
|||
このように、とにかくstatic宣言された変数は、標準CやC++でいうグローバル変数的に振る舞います。 |
|||
static 変数にアクセスする場合、インスタンスの作成は不要です。 |
|||
また、相手先のメソッドの位置さえ分かるように呼び出せば、そのメソッドがpublicならアクセスできます。呼び出し元がどこのクラスにいるかは、呼び出し方法の指定には基本、関係ありません。 |
|||
後述のクラスでも同様、相手先の変数がどのクラスにいるかさえ分かるように指定すれば、事前に適切な処理がされていれば、その変数にアクセスできます。 |
|||
上記の説明では不要だってので説明省略しましたが、C#のメソッドは戻り値(返却値、返り値)をひとつ持てます。C++などと同様です。 |
|||
戻り値の型 メソッド名 (引数1の型 引数名1, 引数2の型 引数名2 ) { |
|||
のような書式です。void は、「戻り値は無し」を意味します。 |
|||
引数が特に無い場合、下記のように、引数名とその型を省略できます。 |
|||
void testfunc (){ |
|||
=== クラス === |
|||
C++でいうクラスは、データの集合をあらわすものの事である標準C言語とC++でいう「構造体」も、若干の違いはあるがクラスと似たようなものである。 |
|||
だがC#は理念として、あらゆるものをクラスまたは構造体で管理するという方針があるので、C++的なクラスの性質だけでなく、C#特有の独特の性質がある。 |
|||
==== 基本 ==== |
|||
クラスの基本的な使いかたは下記のとおり。 |
|||
コード例<syntaxhighlight lang="csharp"> |
|||
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); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
; 実行結果 :<syntaxhighlight lang="text"> |
|||
牛乳 |
|||
140 |
|||
</syntaxhighlight> |
|||
上記コードでいう <code>string name;</code> や <code>int price;</code> などをメンバという。C言語の構造体やC++の構造体/クラスでも同様の働きをするものを「メンバ」と言う。C#の用語はC++を踏襲したものである。 |
|||
メンバは public にアクセス修飾子を指定しないかぎり、基本的には直接はアクセスできない。 |
|||
クラス側の変数宣言において、static 宣言されていない変数は、インスタンスを作成しないかぎり、使うことは出来ません。 |
|||
インスタンスの生成には、new キーワードを使います。上記コードでいう、 |
|||
<syntaxhighlight lang="csharp"> |
|||
testclass a = new testclass() ; |
|||
</syntaxhighlight> |
|||
がインスタンスの生成です。書式は、 |
|||
<syntaxhighlight lang="csharp"> |
|||
クラス名 変数名 = new クラス名() ; |
|||
</syntaxhighlight> |
|||
です。 |
|||
左辺の「変数名」をクラス変数とかインスタンス変数とか呼びたくなりますが、しかし既に「クラス変数」「インスタンス変数」はプログラミング界隈では別の意味で使われている用語ですので、ここでは単に「変数」「変数名」と呼ぶことにしましょう。 |
|||
また、別々のインスタンスに属する変数は、それぞれ別の変数です。単に、「クラス」とは型の情報や初期値などを提供するだけです。つまりクラスは、変数の集まりをつくるときのメモ帳です。 |
|||
個々の変数の実体は、原則的に、(クラス側ではなく)インスタンス側でされています。 |
|||
試しに、複数のインスタンスを作って確認してみましょう。 |
|||
コード例<syntaxhighlight lang="csharp"> |
|||
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); |
|||
Console.WriteLine(); |
|||
// 別インスタンスを作成 |
|||
testclass b = new testclass() ; |
|||
b.name = "みかんジュース"; |
|||
b.price = 120; |
|||
Console.WriteLine(b.name); |
|||
Console.WriteLine(b.price); |
|||
Console.WriteLine(); |
|||
// 牛乳の情報が残ってるかの確認 |
|||
Console.WriteLine(a.name); |
|||
Console.WriteLine(a.price); |
|||
Console.WriteLine(); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
; 実行結果 :<syntaxhighlight lang="text"> |
|||
牛乳 |
|||
140 |
|||
みかんジュース |
|||
120 |
|||
牛乳 |
|||
140 |
|||
</syntaxhighlight> |
|||
このように、最低でも宣言したインスタンスのぶんだけ、変数は新規に確保されます。また、別々のインスタンスにもとづく変数は、それぞれ別個の変数です。なので、上記コードでは「みかんジュース」を宣言しようが「牛乳」はそのまま問題なく残りつづけます。 |
|||
==== コンストラクタ ==== |
|||
下記コードのように、構造体またはクラス中に、構造体名あるいはクラス名と同じメソッド名をもつメソッドを書くと、その構造体/クラスを呼び出したときの処理を指定できる。 |
|||
コード例<syntaxhighlight lang="csharp"> |
|||
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(); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
; 実行結果 :<syntaxhighlight lang="text"> |
|||
牛乳 |
|||
150 |
|||
コーヒー牛乳 |
|||
180 |
|||
牛乳 |
|||
150 |
|||
</syntaxhighlight> |
|||
== クラスと構造体の違い == |
|||
論より証拠、実際に構造体とクラスのコードをそれぞれ実行して結果を比べてみましょう。 |
|||
まず、<code>milk.price = 150;</code>のようにメンバに直接に代入する場合は、クラスでも構造体でも結果は下記のように同じです。 |
|||
コード例<syntaxhighlight lang="csharp"> |
|||
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); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
; 実行結果 :<syntaxhighlight lang="text"> |
|||
コーヒー牛乳 |
|||
180 |
|||
</syntaxhighlight> |
|||
コード例<syntaxhighlight lang="csharp"> |
|||
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); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
; 実行結果 :<syntaxhighlight lang="text"> |
|||
コーヒー牛乳 |
|||
180 |
|||
</syntaxhighlight> |
|||
では違いはというと、・・・※調査中 |
|||
{{NDC|007.64}} |
{{NDC|007.64}} |
2022年6月18日 (土) 12:19時点における版
C#とは、マイクロソフトによって開発されたプログラミング言語です。主な特徴としては、Javaに類似した言語構文を採用していること、.NET Coreというマイクロソフトが策定した共通言語基盤で動作することです。
目次
- インストール方法とそれぞれの実行方法
- C Sharp/Hello World
- C Sharp/変数
- C Sharp/クラスとメソッド
- C Sharp/条件分岐
- C Sharp/ループや繰り返し
※編集中
C# はGUIも作成できますが、本ページでは説明の初期のうちは、コンソールアプリ(DOSプロンプトのような画面にテキスト表示するプログラムのこと)のプログラムを解説したいと思います。
C++になくてC#にある機能は多々ありますが、特にC++との違いが大きい話題を挙げたいと思います。
なお、C# は、Visual C# とは別言語です。ネット上には、Visual C# でしか通用しない話題を「C#」の名前で説明しているものもありますが、不正確な説明です。
ここでいう Visual c# とは、マイクロソフト社がWindowsアプリをC#風の言語で開発できるようにしたマイクロソフト独自言語のことです。もしかしたらマイクロソフト自身、Visual C# の意味で「C#」と説明しているかもしれませんが、単なるC#自身の国際規格には、特にwindowsアプリに特化した仕様・規程などは無いと思われます。
C# は現在では国際規格化されており、マイクロソフトの仕様とは独自に国際規格が存在しています。
書式指定
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式の結果(右側)では、命令ではなく値だけを指定して事前に用意された変数に代入することになる。別途、その結果を代入した変数をつかって目的の処理をする命令が必要である。
※ オブジェクト的なもの
- ※ あとで適切な題名を考える。暫定的なタイトル。
- ※ 「クラスはオブジェクトではないのか?」とか物議を呼びそうだが、説明の都合上、クラスには専用の節があるし、C++にもクラスはあるので、この節ではクラスについては説明しない。文句があるならタイトルを考えて。
C#には、プログラミングを効率化するために、あらかじめ、よく使いそうな処理をまとめたものが提供されています。
標準C言語やC++にはないが、よく使われる機能が、言語仕様として提供されています。
それを使わずとも、C言語などにある機能だけでもプログラミングは可能ですが、しかしC#の提供する専用機能を使うことで、コードが短くなったり、集団開発ではプログラマーたちによるバラツキが無くなるので品質が一様化して効率化したり、いろいろと便利です。また、コードの各部の意図も、C#の専用機能をつかうことで明確化するので効率化します。
例外的に標準C言語との互換性の高いコードを書きたい場合でもないかぎり、積極的にC#の専用機能をつかうことで、コードの短縮や品質安定につながるでしょう。
タプル
複数の変数を宣言する際、タプル 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 公式サイトを見てください。
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 はそのままです。