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 / 1002);
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 / 1002);
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 のキーボードショートカット


この記事が気に入ったらサポートをしてみませんか?