C++プログラマーがC#を振り返る 【働学併進#002】
C#なんて一体いつ使うのか?私の場合は「仕事で」である。
正確には「2年弱ぶりに、Windowsアプリ開発プロジェクトで」である。
なので私は1日でも早くC#を思い出したい。
コーディングしているときに「これってC#でどうやって書くっけ?」と迷った時にMicrosoftの長すぎるドキュメントを見返す必要を無くしたい。
というわけで早速本題に入る。
C++ イディオム vs. C# イディオム
念のため説明すると、イディオム Programming Idiomとは、そのプログラミング言語の「特別な意味や処理などを表す合言葉」である。
さらに蛇足を重ねると、vs.という言葉から「戦う against」という意味を連想するかもしれないが、ここでは「比較する in contrast to」という意味で用いる。
main関数
class ProjectName
{
static public void Main(String[] args)
{
…
}
}
コマンドライン引数を用いない場合はもちろんMain()だけで良い。
コマンドライン引数の要素数は、Lengthプロパティから取得できる。args.Lengthのように。
変数・型
変数の定義方法はC++と同じである。
命名法はキャメルケースcamelCaseが基本であるが、publicな変数や関数、型、namespaceはパスカルケースPascalCaseが使われる。(Microsoft Guideline)
// TypeName VariableName = InitialValue;
string name = “NariKon”;
float heightCM = 175.5F;
float weightKg = 68.5F;
double bmi = weigtKg / Math.Pow(heightCM / 100, 2);
uint age = 31U;
int deposite = 1_000_000;
bool isHandsome = false;
C#では.NETの型名を使うことができる。以下はC#での型名(左)と.NETでの型名(右)である。
またsuffixとは、数字の後ろにつけるアルファベットのことで、その数字がdoubleかfloatか、intかuintかをわかりやすくする役割がある。
byte, System.Byte (8bit)
sbyte, System.SByte (8bit)
short, System.Int16
ushort System.UInt16
int, SystemInt32
uint, SystemUInt32
suffix: U
long, SystemInt64
suffix: L
ulong, SystemUInt64
suffix: UL
nint, System.IntPtr (環境依存32bit/64bit)
nuint, System.UIntPtr (環境依存32bit/64bit)
char, System.Char (16bit)
U+0000 ~ U+FFFF
float, System.Single (32bit)
有効桁数: 6~9 桁
suffix: F
double, System.Double (64bi)t
有効桁数: 15~17 桁
suffix: なし, D
decimal, System.Decimal (128bit)
有効桁数: 28~29 桁
suffix: M
×10^nを表したいときはEを使う。
const decimal MOLE = 6.022E23M;
// 602200000000000000000000
const double VACUUM_PERMITTIVITY = 8.854E-12;
// 8.854E-12
C#7.0以降では、数を区切るための_や、2進数を表す0b prefixが使える。
最初はどうでもいいことのように感じるだろうが、classやstruct内で定義された変数のことをC#ではフィールド field と呼ぶらしい。なぜだ。
型変換 キャスト
以下のように、サイズの大きい型に、それよりサイズの小さい型の値を代入すると黙示的に(自動で)型変換が行われる。
char CharValue = 'A';
int IntValue = CharValue; // 65
long LongValue = IntValue;
float FloatValue = LongValue;
double DoubleValue = FloatValue;
しかし、この逆方向で変換する場合はConvert.ToChar()などと明示的に型変換を行う必要がある。Toの後に続く型名は先ほど紹介した.NETの型名である。
double DoubleValue = 65.0;
float FloatValue = Convert.ToSingle(DoubleValue);
long LongValue = Convert.ToInt64(FloatValue);
int IntValue = Convert.ToInt32(LongValue);
char CharValue = Convert.ToChar(IntValue);
// 'A'
文字列(string)から数字に変える場合も同様にConvert.ToInt32("10")などと書く。
「doubleを返す関数の値をfloatに直したい」みたいなときはC++のように書くことができる。
float sqrt7 = (float)Math.Sqrt(7);
暗黙型(型推論)
C++ で言うautoは、C#ではvarである。JavaScript使いの方はvar嫌いを克服しよう。また、組み込み型を定義するときはsuffixをつける癖を身につけよう。
var name = “NariKon”;
var heightCm = 175.5F;
var weightKg = 68.5F;
var bmi = weigtKg / Math.Pow(heightCm / 100, 2);
var age = 31U;
var deposite = 1_000_000;
var isHandsome = true;
標準入力・標準出力
コマンドラインからの入力を読み込むにはSystem.Console.ReadLine()、コマンドラインに出力するときはSystem.Console.WriteまたはSystem.Console.WriteLineを使う。WriteLineは文末に自動で改行コードを入れてくれる。
System.Console.Write(“What is your name?\t: ”);
string Name = System.Console.ReadLine();
System.Console.WriteLine($“Nice to meet you, {Name}.”);
System.Console.Write(“How old are you?\t:”);
UInt Age = Convert.ToUInt32(Console.ReadLine());
読者の方にはMay I ask your name?くらい丁寧な英語を心がけて頂きたい。
ご覧の通りC#では、$"{VariableName}"で、文字列補間 string interpolation を表す。
この他にも、コマンドラインに表示した内容を消すConsole.Clear() や、ユーザーが入力したキーを受け取るConsole.ReadKey()などがある。
ConsoleKeyInfo cki;
Console.TreatControlCAsInput = true; // CTL+C で処理を終了しない
Console.WriteLine("Press any combination of CTL, ALT, and SHIFT, and a console key.");
Console.WriteLine("Press the Escape (Esc) key to quit: \n");
do {
cki = Console.ReadKey(true);
Console.Write("You pressed ");
if ((cki.Modifiers & ConsoleModifiers.Alt) != 0) Console.Write("ALT+");
if ((cki.Modifiers & ConsoleModifiers.Shift) != 0) Console.Write("SHIFT+");
if ((cki.Modifiers & ConsoleModifiers.Control) != 0) Console.Write("CTL+");
Console.WriteLine($"{cki.Key} (character '{cki.KeyChar}')");
} while (cki.Key != ConsoleKey.Escape);
// You pressed CTL+A (character '☺')
// You pressed CTL+C (character '♥')
// You pressed K (character 'k')
// You pressed ALT+SHIFT+H (character 'H')
// You pressed Escape (character '←')
定数
const はコンパイル時定数。
readonly は実行時定数。
残念ながら、どちらもvarは使えない。
また、readonly は関数内では使えないので、Main関数の外、クラスの中に書く。
条件分岐
ifの使い方はC++と同様である。
if (isHandsome && deposite > 10_000_000) {
Console.WriteLine("お洒落なバー、知ってますよ。");
} else if (isHandsome || deposite > 50_000_000) {
Console.WriteLine("この後、お時間あります?");
} else {
Console.WriteLine("………………………………何か?");
}
C++の三項演算子も同じように使える。
Console.WriteLine(isHandsome ? "YES♡" : "Hell NO");
繰り返し処理
forも、C++と同じように、古典的なループやranged-forループが書ける。
ついでに、配列の書き方も似ているのでまとめる。さらについでにListも。
bool[] isPrime = new bool[n + 1];
Array.Fill(isPrime, true);
for (int p = 2; p * p <= n; p++)
{
if (isPrime[p])
{
for (int i = p * p; i <= n; i += p)
isPrime[i] = false;
}
}
List<int> primeNumbers = new List<int>();
for (int i = 2; i <= n; i++)
{
if (isPrime[i]) primeNumbers.Add(i);
}
foreach (int p in primeNumbers) {
Console.Write($"{p}: √{p} = {Math.Sqrt(p)");
}
配列の定義方法は以下の三つ。どれも生成される配列は整数の配列で要素も同じである。
var list1 = new int[5];
var list2 = { 0, 0, 0, 0, 0 };
var list3 = Enumerable.Repeat(0, 5).ToArray();
配列の長さが予めわからない場合はList を使うと良い。配列arrayのToList関数を使うとList型に変換することができる。
var intArray = {3, 1, 4, 1, 5, 9};
var intList = intArray.ToList();
while, do-whileは全く同じなので省略する。
関数 メソッド
関数も特別変わった定義方法ではない。
多くの関数 Function がclass内にあることからメソッド Methodと呼ばれることが多い。
ReturnType FunctionName(ParamType param1, ParamType param2) {
return returnValue;
}
ただ、Main関数はstaticであるため、Main関数内で呼び出す関数はstatic にする必要がある。
C++では面倒だった複数の値を返す場合はTupleを用いる。
// C# 7.0 or later
(ReturnType1, ReturnType2, ReturnType3) FindTop3() {
...
return (book1, book2, book3);
}
// before C# 7.0
Tuple<ReturnType1, ReturnType2, ReturnType> FindTop3() {
return Tuple.Create(book1, book2, book3);
}
その他にも、C++でいう参照渡し(&)も、ref, out, in といった修飾子で実現できる。違いは以下の通り。
ref: 呼び出し側での初期化が必須。メソッド内での代入は任意。
out: 呼び出し側での初期化が任意。メソッド内での初期化/代入は必須。
in: 呼び出し側での初期化が必須。メソッド内での代入は禁止。
サンプルコードは「クラス」セクションにて。
無名関数 ラムダ
基礎となる書き方は以下の通り。
(param) => expression
// or
(param) => {
statement1;
statement2;
...
}
引数が一つだけなら()はいらない。逆に()は引数を取らないラムダという意味。
int[] numbers = { 2, 3, 4, 5 };
var squaredNumbers = numbers.Select(x => x * x);
Console.WriteLine(string.Join(" ", squaredNumbers));
// Output:
// 4 9 16 25
クラス
struct, class, enumも、特にC++と差分なく書くことができる。
public enum Gender {
Male, Female, Other
}
public class Person {
public string Name { get; set; }
public int Age { get; set;}
// set default value C# 6.0 or later
public Gender Gender { get; set; } = Gender.Other;
public Person() {}
public Person(string name, int age) {
Name = name;
Age = age;
}
public override string ToString()
{
return "Person: " + Name + " " + Age;
}
}
public class ProjectName {
static public void ShowWelcomeMessage(in Person person) {
Console.WriteLine($"Welcome, {person.Name}!");
}
static public void Main() {
// constructor
var narikon = new Person("narikon", 32);
// object initializer
var narikon = new Person { Name = "narikon", Age = 32 };
ShowWelcomeMessage(in narikon);
Console.WriteLine(narikon);
// Person: narikon 32
}
}
C++よりもだいぶ楽をしている印象がある。
プロパティの書き込みを禁止したい場合は以下のように書くが、初期化の際に楽できない(オブジェクト初期化子が使えない)。C# 9.0から追加されたinit修飾子を使えば楽できる。
public Person {
// before C# 9.0
public string Name { get; }
public int Age { get; set;}
public Person(string name) {
Name = name;
}
// C# 9.0 or later
public string Name { get; init;}
public int Age { get; set;}
}
// before C# 9.0
var person = new Person("narikon") { Age = 32 };
// C# 9.0 or later
var person = new Person { Name = "narikon", Age = 32 };
継承の方法はC++と同じであるため省略する。
だいぶ長くなってしまったため、以下の内容は明日にまとめることにする。
LINQ
その他の型
NULLの扱い
C#とNETとNET Coreの違い
C#の独特な名前(フィールド、メソッドなど)
Visual Studio for Mac のキーボードショートカット
この記事が気に入ったらサポートをしてみませんか?