C#: 高度な機能の概要

1. 振り返り

リフレクションにより、プログラムは実行時に自身の型情報を検査・操作することができます。Typeクラスはリフレクションの中核をなすもので、フィールド、プロパティ、メソッドなどのメタデータへのアクセスを提供します。

(1) 型情報の取得

CSHARP
using System;
using System.Reflection;

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public void SayHello() => Console.WriteLine("Hello!");
}

class Program
{
    static void Main()
    {
        Type type1 = typeof(Person);
        Type type2 = new Person().GetType();

        Console.WriteLine($"Type name: {type1.Name}");

        Console.WriteLine("Properties:");
        foreach (PropertyInfo p in type1.GetProperties())
            Console.WriteLine($"  {p.Name} ({p.PropertyType.Name})");

        Console.WriteLine("Methods:");
        foreach (MethodInfo m in type1.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
            Console.WriteLine($"  {m.Name}");
    }
}
TEXT 📖 参照専用
Type name: Person
Properties:
  Name (String)
  Age (Int32)
Methods:
  get_Name
  set_Name
  get_Age
  set_Age
  SayHello

(2) インスタンスの動的な作成

CSHARP
using System;
using System.Reflection;

class Dog
{
    public string Breed { get; set; } = "Unknown";
    public override string ToString() => $"Dog: {Breed}";
}

class Program
{
    static void Main()
    {
        Type type = typeof(Dog);
        object obj = Activator.CreateInstance(type)!;

        PropertyInfo prop = type.GetProperty("Breed")!;
        prop.SetValue(obj, "Husky");

        Console.WriteLine(obj);
    }
}
TEXT 📖 参照専用
Dog: Husky

▶ サンプル

CSHARP
using System;
using System.Linq;
using System.Reflection;

class Calculator
{
    public int Add(int a, int b) => a + b;
    public double Multiply(double x, double y) => x * y;
    public void Reset() { }
}

class Program
{
    static void Main()
    {
        Calculator calc = new Calculator();
        Type type = calc.GetType();

        Console.WriteLine($"Type: {type.Name}");
        Console.WriteLine($"Namespace: {type.Namespace}");
        Console.WriteLine($"Is class: {type.IsClass}");

        Console.WriteLine("Public methods:");
        MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        foreach (MethodInfo m in methods)
        {
            string parameters = string.Join(", ", m.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"));
            Console.WriteLine($"  {m.ReturnType.Name} {m.Name}({parameters})");
        }
    }
}
▶ 試してみよう
TEXT 📖 参照専用
Type: Calculator
Namespace: 
Is class: True
Public methods:
  Int32 Add(Int32 a, Int32 b)
  Double Multiply(Double x, Double y)
  Void Reset()

2. 属性

属性とは、コード要素にメタデータを追加するために使用される宣言型タグのことです。組み込みの属性を使用することも、カスタム属性を定義することもできます。

(1) 組み込み属性

CSHARP
using System;

class LegacyService
{
    [Obsolete("Please use NewService instead", error: false)]
    public void OldMethod() => Console.WriteLine("Old method");

    public void NewMethod() => Console.WriteLine("New method");
}

class Program
{
    static void Main()
    {
        var service = new LegacyService();
        service.OldMethod();
        service.NewMethod();
    }
}
TEXT 📖 参照専用
Old method
New method
💡 Obsoleteerrorパラメータがtrueに設定されている場合、旧式のメソッドを呼び出すと、警告ではなくコンパイルエラーが発生します。

(2) カスタム属性

CSHARP
using System;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
class DescriptionAttribute : Attribute
{
    public string Text { get; }
    public DescriptionAttribute(string text) => Text = text;
}

[Description("User management service")]
class UserService
{
    [Description("Get all users")]
    public void GetAll() { }
}

class Program
{
    static void Main()
    {
        var attr = Attribute.GetCustomAttribute(typeof(UserService), typeof(DescriptionAttribute)) as DescriptionAttribute;
        Console.WriteLine(attr?.Text ?? "No description");
    }
}
TEXT 📖 参照専用
User management service

3. ジェネリック制約の詳細

ジェネリック制約では、whereキーワードを使用して型パラメータの範囲を制限し、それらが特定の条件を満たすようにします。

(1) 制約タイプの概要

制約 説明
where T : class T は参照型でなければならない
where T : struct T は値型でなければならない(null にはできない)
where T : new() T には引数なしのパブリックコンストラクタが必須である
where T : ISpecific T は指定されたインターフェースを実装しなければならない
where T : BaseClass T は指定された基底クラスを継承していなければならない
where T : unmanaged T は非管理型でなければならない
where T : notnull T は null を許さない型でなければならない

▶ サンプル

CSHARP
using System;

interface IRepository
{
    string GetName();
}

class SqlRepository : IRepository
{
    public string GetName() => "SQL Repository";
}

class Service```<T>``` where T : class, IRepository, new()
{
    private T _repo = new T();
    public void Print() => Console.WriteLine(_repo.GetName());
}

class Program
{
    static void Main()
    {
        var service = new Service<SqlRepository>();
        service.Print();
    }
}
▶ 試してみよう
TEXT 📖 参照専用
SQL Repository
⚠️ new()という制約は、制約リストの最後に配置する必要があります。最初にclassまたはstructを配置してください。


4. イテレータ(yield)

イテレータはyield returnを使用してシーケンスの要素を1つずつ生成し、遅延評価を実現しています。つまり、要素は反復処理が行われるときにのみ計算されます。

(1) 利回り(yield return)と利回りブレイク(yield break)

CSHARP
using System;
using System.Collections.Generic;

class Program
{
    static IEnumerable```<int>``` GetNumbers()
    {
        for (int i = 0; i < 5; i++)
        {
            if (i == 3) yield break;
            yield return i;
        }
    }

    static void Main()
    {
        foreach (int n in GetNumbers())
            Console.WriteLine(n);
    }
}
TEXT 📖 参照専用
0
1
2
💡 yield break は反復処理を直ちに終了させます。イテレータのメソッドは IEnumerable<T> または IEnumerator<T> を返します。

▶ サンプル

CSHARP
using System;
using System.Collections.Generic;

class Program
{
    static IEnumerable```<int>``` Fibonacci(int count)
    {
        int a = 0;
        int b = 1;
        for (int i = 0; i < count; i++)
        {
            yield return a;
            int temp = a + b;
            a = b;
            b = temp;
        }
    }

    static void Main()
    {
        Console.WriteLine("Fibonacci sequence, first 10 terms:");
        foreach (int n in Fibonacci(10))
        {
            Console.Write(n + " ");
        }
        Console.WriteLine();
    }
}
▶ 試してみよう
TEXT 📖 参照専用
Fibonacci sequence, first 10 terms:
0 1 1 2 3 5 8 13 21 34 

5. スパン<T>とメモリ<T>

Span<T>およびMemory<T>は、連続したメモリ領域へのコピー不要のスライスアクセスを提供するため、高性能が求められるシナリオにおいて重要な型となっています。

(1) Span<T> 基本的な使い方

CSHARP
using System;

class Program
{
    static void Main()
    {
        int[] array = { 10, 20, 30, 40, 50 };
        Span```<int>``` span = array.AsSpan();
        Span```<int>``` slice = span.Slice(1, 3);

        slice[0] = 99;

        Console.WriteLine(string.Join(", ", array));

        Span```<int>``` stackSpan = stackalloc int[3];
        stackSpan[0] = 1;
        stackSpan[1] = 2;
        stackSpan[2] = 3;
        Console.WriteLine(string.Join(", ", stackSpan.ToArray()));
    }
}
TEXT 📖 参照専用
10, 99, 30, 40, 50
1, 2, 3
⚠️ Span<T>はスタック上にのみ格納可能であり、クラスフィールドとして使用したり、awaitの境界を越えて使用したりすることはできません。スタックフレームをまたぐ必要がある場合は、Memory<T>を使用してください。


6. 正規表現(Regex)

System.Text.RegularExpressions.Regexクラスは、テキストのパターンマッチング、検索、および置換に使用されます。

(1) 一般的な操作

CSHARP
using System;
using System.Text.RegularExpressions;

class Program
{
    static void Main()
    {
        string input = "Contact phone: 138-1234-5678, Email: test@example.com";

        bool hasPhone = Regex.IsMatch(input, @"\d{3}-\d{4}-\d{4}");
        Console.WriteLine($"Contains phone: {hasPhone}");

        Match phoneMatch = Regex.Match(input, @"\d{3}-\d{4}-\d{4}");
        Console.WriteLine($"Phone: {phoneMatch.Value}");

        MatchCollection emails = Regex.Matches(input, @"[\w.]+@[\w.]+");
        foreach (Match m in emails)
            Console.WriteLine($"Email: {m.Value}");

        string replaced = Regex.Replace(input, @"\d{4}", "****");
        Console.WriteLine($"Replaced: {replaced}");
    }
}
TEXT 📖 参照専用
Contains phone: True
Phone: 138-1234-5678
Email: test@example.com
Replaced: Contact phone: 138-****-****, Email: test@example.com

(2) 一般的なパターンのクイックリファレンス

パターン 説明
\d 数字
\w 英字・数字・アンダースコア
\s 空白
. 任意の文字(改行を除く)
^ / $ 行の先頭 / 行の末尾
{n,m} nをm回繰り返す
[abc] 文字セット

7. 安全でないコード

unsafeコンテキストでは、ポインタや手動によるメモリ操作の使用が可能であり、相互運用や極限のパフォーマンスが求められるシナリオに適しています。

(1) ポインタと固定

CSHARP
using System;

class Program
{
    static unsafe void Main()
    {
        int value = 42;
        int* ptr = &value;

        Console.WriteLine($"Value: {*ptr}");
        Console.WriteLine($"Address: {(long)ptr}");

        int[] arr = { 1, 2, 3 };
        fixed (int* p = arr)
        {
            Console.WriteLine($"First element: {*p}");
            Console.WriteLine($"Second element: {*(p + 1)}");
        }
    }
}
TEXT 📖 参照専用
Value: 42
Address: 12345678
First element: 1
Second element: 2
⚠️ unsafeを使用するには、<AllowUnsafeBlocks>true</AllowUnsafeBlocks>`` in the project file. The fixed` ステートメントでオブジェクトを固定し、GCによるメモリの移動を防ぐ必要があります。


8. C# バージョンの機能概要 (7–12)

バージョン 主な機能 概要
C# 7 out 変数、タプル、パターンマッチング out int x インライン宣言、(int, string) タプル、is Type パターン
C# 7 ローカル関数、参照型戻り値 メソッド内のネストされた関数、参照型戻り値
C# 8 ヌル許容参照型、非同期ストリーム string? ヌル許容アノテーション、IAsyncEnumerable<T>
C# 8 インターフェースのデフォルトメソッド、インデックス、および範囲 インターフェースのデフォルト実装、[^1][1..3]
C# 9 レコード型、初期化セッター record 不変型、init 初期化専用プロパティ
C# 9 トップレベル文、ターゲット型指定の new Main の定型コード不要、new() 型の省略
C# 10 グローバルな using 宣言、ファイルスコープのネームスペース global using X;, namespace Ns; 1行
C# 10 レコード構造体、定数補間文字列 record struct、定数補間文字列
C# 11 生の文字列リテラル、必須 """...""" 複数行の生の文字列、required 強制初期化
C# 11 リストパターン、UTF-8 リテラル [1, 2, ..] マッチング、"hello"u8
C# 12 プライマリコンストラクタ class C(int x) コンストラクタとしてのクラス宣言
C# 12 コレクション式、任意の型のエイリアス [1, 2, 3] 一括初期化、using P = int*
💡 このレッスンでは、高度な機能の概要を説明します。各トピックについては、今後の専用レッスンで詳しく取り上げます。

❓ よくある質問

Q リフレクションにはパフォーマンス上のオーバーヘッドがありますか?
A はい、リフレクションは直接呼び出しに比べて数十倍遅くなります。頻繁に使用する場合は、MethodInfo をキャッシュするか、式ツリーに切り替えてください。
Q Span<T>とMemory<T>の違いは何ですか?
A Spanはスタック上にのみ存在でき、非同期/ストレージの境界を越えることはできません。一方、Memoryはヒープ上に存在でき、非同期の境界を越えることができ、.Spanを介してアクセスされます。
Q yield のリターンはマルチスレッド対応ですか?
A いいえ、yield はシングルスレッドの遅延列挙メカニズムです。要素は MoveNext() が呼び出されたときにのみ計算されます。
Q どのような場合に「安全でないコード」が必要になるのでしょうか?
A C/C++との相互運用、アンマネージドメモリ操作、あるいは極限のパフォーマンスが必要な場合に使用してください。日常的な開発では使用を避けてください。

📖 まとめ

📝 練習問題

  1. リフレクションを使用して、stringクラスのすべてのパブリックメソッド名を取得し、それらを出力する
  2. カスタム属性[Author]を作成し、クラスに適用して、リフレクションを使って読み取る
  3. インスタンスを作成して返す汎用メソッドT Create``<T>``() where T : new()を作成する
  4. yield return を使用して、フィボナッチ数列の最初の 20 項を生成するイテレータを実装してください。
  5. 正規表現(Regex)を使用して、文字列が有効なIPv4アドレスの形式であるかどうかを検証する
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%