C Sharp/式と演算子

出典: フリー教科書『ウィキブックス(Wikibooks)』

C#の式と演算子[編集]

式 (Expressions)[編集]

C#プログラミングでは、式は値を生成するコードの断片です。式は変数、リテラル、演算子、メソッドの呼び出し、式の組み合わせなどから構成されます。例えば、x + y10 * (5 - 3)someFunction()などが式です。

プリミティブな式[編集]

  1. 変数とリテラル: 変数は値を保持し、リテラルは直接的な値を表します。例えば、int age = 30;という宣言では、30はリテラルです。
  2. 演算子: C#には算術演算子 (+, -, *, / など) や比較演算子 (==, !=, <, > など) があります。これらの演算子を使用して式を作成できます。

演算子 (Operators)[編集]

算術演算子[編集]

int a = 10;
int b = 5;
int sum = a + b; // 加算演算子
int difference = a - b; // 減算演算子
int product = a * b; // 乗算演算子
int quotient = a / b; // 除算演算子
int remainder = a % b; // 剰余演算子

比較演算子[編集]

int x = 10;
int y = 20;
bool isEqual = (x == y); // 等号演算子
bool isNotEqual = (x != y); // 不等号演算子
bool isGreater = (x > y); // より大きい演算子
bool isLess = (x < y); // より小さい演算子
bool isGreaterOrEqual = (x >= y); // 以上演算子
bool isLessOrEqual = (x <= y); // 以下演算子

代入演算子[編集]

int number = 10;
number += 5; // number = number + 5;
number -= 3; // number = number - 3;
number *= 2; // number = number * 2;
number /= 4; // number = number / 4;
number %= 3; // number = number % 3;

論理演算子[編集]

bool condition1 = true;
bool condition2 = false;
bool resultAnd = condition1 && condition2; // 論理積演算子 (AND)
bool resultOr = condition1 || condition2; // 論理和演算子 (OR)
bool resultNot = !condition1; // 論理否定演算子 (NOT)
bool resultOr = condition1 != condition2; // 排他的論理和演算子 (XOR)

ビット演算子[編集]

ビット演算子は、整数のビットレベルでの操作を行います。主なビット演算子には以下のものがあります。

int a = 5; // 0101
int b = 3; // 0011

int andResult = a & b; // AND演算子: 0001 (1)
int orResult = a | b; // OR演算子: 0111 (7)
int xorResult = a ^ b; // XOR演算子: 0110 (6)
int notResult = ~a; // NOT演算子: 1010 (-6)
int leftShiftResult = a << 1; // 左シフト演算子: 1010 (10)
int rightShiftResult = a >> 1; // 右シフト演算子: 0010 (2)

三項演算子[編集]

三項演算子は、条件に応じて値を選択するための短縮形です。

int age = 18;
string message = (age >= 18) ? "成人です" : "未成年です";
// 条件が真の場合は "成人です" が、偽の場合は "未成年です" が代入される

Null 合体演算子[編集]

Null 合体演算子 (??) は、Nullチェックとデフォルト値の指定を同時に行います。

string name = null;
string result = name ?? "No Name";
// name が null でない場合は name、null の場合は "No Name" が result に代入される

Null 条件演算子 (?.)[編集]

この演算子は、オブジェクトのメンバーにアクセスする際に、そのオブジェクトが null でない場合にのみアクセスを試みるものです。

// C#の例
string name = person?.Name;
// person が null でない場合にのみ、Name プロパティにアクセスする

ラムダ演算子 (=>)[編集]

C#のラムダ演算子は、無名関数やクロージャを表現するためのもので、C言語にはありません。LINQクエリやデリゲートと組み合わせて使用されることが一般的です。

// C#の例
Func<int, int> square = x => x * x;
// x を受け取り、その二乗を返す無名関数を定義

is 演算子[編集]

is 演算子は、特定の型に変換可能かどうかをチェックするための演算子です。この演算子を使用すると、特定のオブジェクトが指定された型に変換可能かどうかを確認できます。

例えば:

object obj = "Hello";

if (obj is string) {
    Console.WriteLine("obj は string 型です。");
} else {
    Console.WriteLine("obj は string 型ではありません。");
}

この場合、objstring 型に変換可能であるかどうかを確認しています。もし変換可能であれば、if 文の中のブロックが実行されます。

is 演算子は、安全な型キャストを行う前に、変換が可能かどうかをチェックするためによく使われます。これにより、無効な型キャストによるエラーや例外を避けることができます。

この演算子を使用すると、あるオブジェクトが特定の型に変換可能かどうかを効率的に確認できます。

as 演算子[編集]

as 演算子は、参照型のオブジェクトを指定された型に安全にキャストするための演算子です。as を使うと、変換が失敗した場合に例外がスローされるのではなく、null が返されます。

例えば:

object obj = "Hello";

string str = obj as string;

if (str != null) {
    Console.WriteLine("obj を string にキャストしました。");
} else {
    Console.WriteLine("obj を string にキャストできませんでした。");
}

この例では、obj を string 型にキャストしています。もし obj が string であれば、str には obj の値がキャストされ、if 文の中のブロックが実行されます。しかし、もし obj が string でない場合は、as 演算子は null を返し、str は null となります。

as 演算子は型の安全なキャストを行う際に使用され、キャストが失敗しても例外をスローせずに null を返すため、条件分岐や安全な型変換に便利です。

nameof 演算子[編集]

nameof 演算子は、コード内の識別子(変数、型、メンバーなど)の名前を文字列として取得するための演算子です。これは、リファクタリングやデバッグ時に特に役立ちます。

例えば:

string propertyName = nameof(MyClass.SomeProperty);

このようにすることで、MyClass クラスの SomeProperty プロパティの名前が文字列として propertyName に代入されます。もし SomeProperty の名前が変更されても、コンパイル時にエラーを検知できます。

typeof 演算子[編集]

typeof 演算子は、型情報を取得するための演算子です。これは主にリフレクションやジェネリック型などで使用されます。

例えば:

Type type = typeof(MyClass);

このようにすることで、MyClass の型情報が type 変数に代入されます。これを使用すると、実行時に型情報を取得し、それに基づいて操作を行うことができます。

sizeof 演算子[編集]

sizeof 演算子は、指定された型のバイト数を取得するための演算子です。ただし、この演算子は主に値型(プリミティブ型)のサイズを取得するために使用されます。

int sizeOfInt = sizeof(int);

この例では、int 型のバイト数が sizeOfInt に代入されます。ただし、sizeof 演算子は全ての型で使用できるわけではなく、特定の条件を満たす値型にのみ使用可能です。

LINQ演算子[編集]

Language Integrated Query (LINQ) は、C#に組み込まれたクエリ言語であり、データソースからデータをクエリするための構文を提供します。これは、C言語には存在しません。

// C#の例
var filteredList = from number in numbers
                   where number > 5
                   select number;
// numbers リストから条件に合致する要素を抽出する LINQ クエリ

演算子の優先順位[編集]

C#の演算子の優先順位
カテゴリーまたは名前 演算子
プライマリー x.y, f(x), a[i], x?.y, x?[y], x++, x--, x!, new, typeof, checked, unchecked, default, nameof, delegate, sizeof, stackalloc, x->y
単項 +x, -x, !x, ~x, ++x, --x, ^x, (T)x, await, &x, *x, true および false
レンジ x..y
switch および with 式 switch, with
乗除算 x * y, x / y, x % y
加減算 x + y, x - y
シフト x << y, x >> y, x >>> y
関係および型テスト x < y, x > y, x <= y, x >= y, is, as
等価 x == y, x != y
論理 AND またはビット演算 AND x & y
論理 XOR またはビット演算 XOR x ^ y
論理 OR またはビット演算 OR x | y
条件 AND x && y
条件 OR x || y
Null 合体演算子 x ?? y
条件演算子 c ? t : f
代入とラムダ宣言 x = y, x += y, x -= y, x *= y, x /= y, x %= y, x &= y, x |= y, x ^= y, x <<= y, x >>= y, x >>>= y, x ??= y, =>

演算子の結合性[編集]

演算子の結合性(associativity)は、演算子が同じ優先順位を持つ場合に、演算の順序を決定します。

左結合性(Left-associative)
左結合性の演算子は、左から右へ順番に評価されます。代入演算子とnull結合演算子を除いたすべての二項演算子が左結合性です。例えば、x + y - z は (x + y) - z として評価されます。
右結合性(Right-associative)
右結合性の演算子は、右から左へ順番に評価されます。代入演算子、null結合演算子、ラムダ式、三項演算子 ?: は右結合性です。例えば、a = b = c は a = (b = c) として評価されます。

演算子オーバーロード[編集]

C#では、特定の演算子をオーバーロードして、ユーザー定義の型に対してカスタム動作を定義することができます。以下は、C#での演算子オーバーロードの例です。

using System;

class Vector2D {
public
  double X {
    get;
    set;
  }
public
  double Y {
    get;
    set;
  }

public
  Vector2D(double x, double y) {
    X = x;
    Y = y;
  }

  // + 演算子のオーバーロード
public
  static Vector2D operator+(Vector2D v1, Vector2D v2) {
    return new Vector2D(v1.X + v2.X, v1.Y + v2.Y);
  }

  // == 演算子のオーバーロード
public
  static bool operator==(Vector2D v1, Vector2D v2) {
    return v1.X == v2.X && v1.Y == v2.Y;
  }

  // != 演算子のオーバーロード
public
  static bool operator!=(Vector2D v1, Vector2D v2) { return !(v1 == v2); }
}

class Program {
  static void Main(string[] args) {
    Vector2D vector1 = new Vector2D(1, 2);
    Vector2D vector2 = new Vector2D(3, 4);

    // + 演算子の使用
    Vector2D result = vector1 + vector2;
    Console.WriteLine($"Vector Addition: ({result.X}, {result.Y})");

    // == 演算子の使用
    Console.WriteLine($"Vectors are equal: {vector1 == vector2}");

    // != 演算子の使用
    Console.WriteLine($"Vectors are not equal: {vector1 != vector2}");
  }
}

この例では、2次元ベクトルを表すVector2Dクラスを定義し、+演算子、==演算子、!=演算子をオーバーロードしています。これにより、ベクトル同士の加算や等価性の比較がカスタマイズされた方法で実行されます。

C#における演算子オーバーロードの利点はいくつかあります。

表現力とカスタマイズ性
演算子オーバーロードを利用することで、特定のユーザー定義の型に対して、算術演算子や比較演算子などを使った自然な操作を提供できます。たとえば、ベクトルや行列などの数学的な型を扱う場合、+演算子で加算や==演算子で等価性の比較ができるようにカスタマイズすることができます。
読みやすさと保守性
ユーザー定義の型に対して、組み込みの演算子を使用することで、コードがより自然で読みやすくなります。これにより、コードの理解や保守が容易になります。例えば、vector1 + vector2のような記述が数学的な操作を反映していることが直感的にわかります。
統一されたインターフェースの提供
演算子オーバーロードを利用することで、特定の型に対して一貫したインターフェースを提供できます。例えば、異なるベクトル型に対して同じような演算子を使用することで、コードをより一貫性のあるものにすることができます。
例外処理の提供
カスタム演算子をオーバーロードすることで、特定の操作に関連する例外処理を提供することも可能です。たとえば、ゼロ除算などの特定の条件下で例外をスローすることができます。

これらの利点は、特定の型や操作にカスタムな動作を提供することで、コードの効率性、読みやすさ、保守性を高めることができます。ただし、適切に使用しないとコードの理解が難しくなる場合もあるため、適切な場面での使用が重要です。