組み込みの参照型 (C# リファレンス)

C# には多くの組み込みの参照型があります。 それらには、.NET ライブラリでの型に対するシノニムであるキーワードまたは演算子があります。

オブジェクトの型

object 型は .NET での System.Object の別名です。 C# の統一型システムでは、すべての型 (定義済み、ユーザー定義、参照型、および値の型) が、直接または間接的に System.Object を継承します。 object 型の変数には、任意の型の値を割り当てることができます。 すべての object 変数には、リテラル null を使って既定値を割り当てることができます。 値型の変数がオブジェクトに変換されることを、"ボックス化" されると言います。 object 型の変数が値型に変換されることを、"ボックス化解除" されると言います。 詳細については、「ボックス化とボックス化解除」を参照してください。

文字列型

string 型は、0 個以上の Unicode 文字のシーケンスを表します。 string は .NET の System.String の別名です。

string は参照型ですが、等値演算子 == および != は、string オブジェクトの参照ではなく、値を比較するように定義されています。 値ベースの等価性により、文字列が等しいかを直感的にテストできます。 次に例を示します。

string a = "hello";
string b = "h";
// Append to contents of 'b'
b += "ello";
Console.WriteLine(a == b);
Console.WriteLine(object.ReferenceEquals(a, b));

前の例では、文字列の内容が等しいので "True"、"False" の順に表示されますが、a および b は同じ文字列インスタンスを参照しません。

+ 演算子では、文字列が連結されます。

string a = "good " + "morning";

前のコードは、"good morning" を含む文字列オブジェクトを作成します。

文字列は "変更不可" です。文字列オブジェクトの作成後、そのコンテンツを変更することはできません。 たとえば、このコードを作成すると、コンパイラによって新しい文字列オブジェクトを格納する新しいシーケンス オブジェクトが生成され、その新しいオブジェクトが b に割り当てられます。 b に割り当てられたメモリは (文字列 "h" が含まれている場合)、ガベージ コレクションの対象になります。

string b = "h";
b += "ello";

[]演算子は、文字列の各文字への読み取り専用アクセスに使用できます。 有効なインデックス値は 0 から始まり、文字列の長さ未満である必要があります。

string str = "test";
char x = str[2];  // x = 's';

同様に、[] 演算子を使って文字列内の各文字を反復処理することもできます。

string str = "test";

for (int i = 0; i < str.Length; i++)
{
  Console.Write(str[i] + " ");
}
// Output: t e s t

文字列リテラル

文字列リテラルは string 型であり、生、二重引用符で囲む形式、逐語的文字列の 3 種類があります。

生文字列リテラル は、C# 11 以降で使用できます。 生文字列リテラルには、エスケープ シーケンスを必要とせずに任意のテキストを含めることができます。 生文字列リテラルには、空白文字や改行、埋め込み引用符、その他の特殊文字を含めることができます。 生文字列リテラルは、少なくとも 3 つの二重引用符 (""") で囲まれています。

"""
This is a multi-line
    string literal with the second line indented.
"""

3 つ以上の二重引用符文字のシーケンスを含めることもできます。 テキストに引用符の組み込みシーケンスが必要な場合は、必要に応じて、より多くの引用符で生文字列リテラルを開始および終了します。

"""""
This raw string literal has four """", count them: """" four!
embedded quote characters in a sequence. That's why it starts and ends
with five double quotes.

You could extend this example with as many embedded quotes as needed for your text.
"""""

通常、生文字列リテラルには、埋め込みテキストとは別の行に開始および終了引用符シーケンスがあります。 複数行の生文字列リテラルは、それ自体が引用符で囲まれた文字列をサポートします。

var message = """
"This is a very important message."
""";
Console.WriteLine(message);
// output: "This is a very important message."

開始と終了の引用符が別々の行にある場合、開始引用符に続く改行と終了引用符の前の改行は、最終的なコンテンツには含まれません。 終了引用符シーケンスは、文字列リテラルの左端の列を指定します。 生文字列リテラルをインデントして、コード全体の形式に一致させることができます。

var message = """
    "This is a very important message."
    """;
Console.WriteLine(message);
// output: "This is a very important message."
// The leftmost whitespace is not part of the raw string literal

終了引用符シーケンスの右側にある列は保持されます。 この動作により、次の例に示すように、JSON、YAML、XML などのデータ形式の生文字列が有効になります。

var json= """
    {
        "prop": 0
    }
    """;

いずれかのテキスト行が終了引用符シーケンスの左側に拡張された場合、コンパイラはエラーを発行します。 文字列リテラルが引用符文字で開始も終了もしない場合、開始と終了の引用符シーケンスは同じ行に置くことができます。

var shortText = """He said "hello!" this morning.""";

生文字列リテラルと文字列補間 を組み合わせて、出力文字列に引用符文字と中かっこを含めることができます。

二重引用符で囲む場合は、リテラル文字列の前後に二重引用符 (") を付けます。

"good morning"  // a string literal

リテラル文字列には、任意の文字リテラルを含めることができます。 これにはエスケープ シーケンスが含まれます。 次の例では、円記号にエスケープ シーケンス \\、文字 f に \u0066、改行に \n を使用しています。

string a = "\\\u0066\n F";
Console.WriteLine(a);
// Output:
// \f
//  F

注意

エスケープ コード \udddd (dddd は 4 桁の数字) は、Unicode 文字 U +dddd を表します。 8 桁の Unicode エスケープ コード \Udddddddd も認識できます。

verbatim 文字列リテラルの場合は、先頭に @ を付け、さらに前後に二重引用符を付けます。 次に例を示します。

@"good morning"  // a string literal

verbatim 文字列の利点は、エスケープ シーケンスが処理 "されない" ため、記述しやすくなることです。 たとえば、次のテキストは完全修飾 Windows ファイル名と一致します。

@"c:\Docs\Source\a.txt"  // rather than "c:\\Docs\\Source\\a.txt"

@ 引用符で囲まれた文字列に二重引用符を含めるには、二重引用符を二重にします。

@"""Ahoy!"" cried the captain." // "Ahoy!" cried the captain.

UTF-8 の文字列リテラル

.NET の文字列は、UTF-16 エンコードを使用して格納されます。 UTF-8 は、Web プロトコルやその他の重要なライブラリの標準です。 C# 11 以降では、文字列リテラルに u8 サフィックスを追加して UTF-8 エンコードを指定できます。 UTF-8 リテラルは ReadOnlySpan<byte> オブジェクトとして格納されます。 UTF-8 文字列リテラルの自然型は ReadOnlySpan<byte> です。 UTF-8 文字列リテラルを使用すると、次のコードに示すように、同等の System.ReadOnlySpan<T> を宣言するよりも明確な宣言が作成されます。

ReadOnlySpan<byte> AuthWithTrailingSpace = new byte[] { 0x41, 0x55, 0x54, 0x48, 0x20 };
ReadOnlySpan<byte> AuthStringLiteral = "AUTH "u8;

UTF-8 文字列リテラルを配列として格納するには、リテラルを含むバイトを変更可能な配列にコピーするために ReadOnlySpan<T>.ToArray() を使用する必要があります。

byte[] AuthStringLiteral = "AUTH "u8.ToArray();

UTF-8 の文字列リテラルはコンパイル時定数ではありません。これらはランタイム定数です。 したがって、省略可能なパラメーターの既定値として使用することはできません。 UTF-8 の文字列リテラルを文字列補間と組み合わせることはできません。 同じ文字列式で $ トークンと u8 サフィックスを使用することはできません。

デリゲート型

デリゲート型の宣言は、メソッド シグネチャに似ています。 戻り値 1 つのほか、任意の型のパラメーターをいくつでも指定することができます。

public delegate void MessageDelegate(string message);
public delegate int AnotherDelegate(MyType m, long num);

.NET では、System.Action 型と System.Func 型により、多くの一般的なデリゲートに対するジェネリック定義が提供されます。 おそらく、新しいカスタム デリゲート型を定義する必要はありません。 代わりに、提供されたジェネリック型のインスタンス化を作成できます。

delegate は、名前付きメソッドまたは匿名メソッドをカプセル化することができる参照型です。 デリゲートは C++ の関数ポインターに似ていますが、タイプ セーフであり安全です。 デリゲートの使い方については、デリゲート汎用デリゲートに関するページを参照してください。 デリゲートはイベントの土台となる働きをします。 デリゲートは名前付きメソッドまたは匿名メソッドに関連付けることによって、インスタンス化することができます。

デリゲートは、適合する入力パラメーターと戻り値の型を持ったメソッドまたはラムダ式でインスタンス化する必要があります。 メソッドのシグネチャでどの程度の変性が許容されるかについて詳しくは、デリゲートの変性に関するページを参照してください。 匿名メソッドで使用する場合は、デリゲートとそれに関連付けるコードとを一緒に宣言します。

バリアント変換が原因で実行時に関係するデリゲート型が異なる場合、デリゲートの組み合わせや削除はランタイム例外で失敗します。 次に示すのは、失敗する状況の例です。

Action<string> stringAction = str => {};
Action<object> objectAction = obj => {};
  
// Valid due to implicit reference conversion of
// objectAction to Action<string>, but may fail
// at run time.
Action<string> combination = stringAction + objectAction;

新しいデリゲート オブジェクトを作成することにより、適切なランタイム型でデリゲートを作成できます。 次に示すのは、この回避策を前の例に適用する方法です。

Action<string> stringAction = str => {};
Action<object> objectAction = obj => {};
  
// Creates a new delegate instance with a runtime type of Action<string>.
Action<string> wrappedObjectAction = new Action<string>(objectAction);

// The two Action<string> delegate instances can now be combined.
Action<string> combination = stringAction + wrappedObjectAction;

C# 9 以降では、同様の構文を使用する "関数ポインター" を宣言できます。 関数ポインターでは、デリゲート型をインスタンス化して仮想 Invoke メソッドを呼び出す代わりに、calli 命令を使用します。

dynamic 型

dynamic 型は、変数およびそのメンバーに対する参照の使用が、コンパイル時の型チェックをバイパスすることを示します。 代わりに、演算は実行時に解決されます。 dynamic 型により、Office オートメーション API などの COM API、IronPython ライブラリなどの動的 API、および HTML ドキュメント オブジェクト モデル (DOM: Document Object Model) へのアクセスが容易になります。

ほとんどの環境で、dynamic 型は object 型のように動作します。 具体的には、null 以外の任意の式を dynamic 型に変換できます。 dynamic 型は object と異なり、dynamic 型の式を含む演算はコンパイラによって解決および型チェックされません。 コンパイラは演算に関する情報をまとめてパッケージ化します。その情報が後で実行時に演算を評価するために使用されます。 このプロセスの過程で、dynamic 型の変数は object 型の変数にコンパイルされます。 そのため、dynamic 型はコンパイル時にのみ存在し、実行時には存在しません。

dynamic 型の変数と object 型の変数の違いを次に示します。 コンパイル時に各変数の型を確認するには、WriteLine ステートメントの dyn または obj にマウス ポインターを置きます。 IntelliSense が使用可能なエディターに、次のコードをコピーします。 IntelliSense 機能によって、dyn には dynamicobj には object が表示されます。

class Program
{
    static void Main(string[] args)
    {
        dynamic dyn = 1;
        object obj = 1;

        // Rest the mouse pointer over dyn and obj to see their
        // types at compile time.
        System.Console.WriteLine(dyn.GetType());
        System.Console.WriteLine(obj.GetType());
    }
}

WriteLine ステートメントは dyn および obj の実行時の型を表示します。 その時点では、両方が同じ整数型を持ちます。 次の出力が生成されます。

System.Int32
System.Int32

コンパイル時の dynobj の違いを確認するには、前の例の宣言と WriteLine ステートメントの間に次の 2 行を追加します。

dyn = dyn + 3;
obj = obj + 3;

obj + 3 に整数およびオブジェクトを追加しようとしたことに対してコンパイル エラーが報告されます。 ただし、dyn + 3 に関するエラーは報告されません。 dyn を含む式はコンパイル時にはチェックされません。これは、dyn の型が dynamic であるためです。

さまざまな宣言で dynamic を使用する例を次に示します。 また、Main メソッドで、コンパイル時の型チェックと実行時の型チェックの違いを確認できます。

using System;

namespace DynamicExamples
{
    class Program
    {
        static void Main(string[] args)
        {
            ExampleClass ec = new ExampleClass();
            Console.WriteLine(ec.exampleMethod(10));
            Console.WriteLine(ec.exampleMethod("value"));

            // The following line causes a compiler error because exampleMethod
            // takes only one argument.
            //Console.WriteLine(ec.exampleMethod(10, 4));

            dynamic dynamic_ec = new ExampleClass();
            Console.WriteLine(dynamic_ec.exampleMethod(10));

            // Because dynamic_ec is dynamic, the following call to exampleMethod
            // with two arguments does not produce an error at compile time.
            // However, it does cause a run-time error.
            //Console.WriteLine(dynamic_ec.exampleMethod(10, 4));
        }
    }

    class ExampleClass
    {
        static dynamic field;
        dynamic prop { get; set; }

        public dynamic exampleMethod(dynamic d)
        {
            dynamic local = "Local variable";
            int two = 2;

            if (d is int)
            {
                return local;
            }
            else
            {
                return two;
            }
        }
    }
}
// Results:
// Local variable
// 2
// Local variable

C# 言語仕様

詳細については、「C# 言語仕様」の次のセクションを参照してください。

関連項目