SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

【C#で知っておくべき新機能】最新バージョンを徹底解説!

C# 14の新機能を理解する――extensionブロックとnull条件付き代入

【C#で知っておくべき新機能】最新バージョンを徹底解説! 第8回

拡張メンバー(Extension members)

 C# 14では、extensionブロックにより、拡張メソッドに加えて静的メソッド、インスタンスプロパティ、静的プロパティが拡張メンバーとして使えるようになりました。

 C#には拡張メソッドと呼ばれるものがあり、別クラスで定義した静的メソッドをインスタンスメソッドのように呼び出すことができます。

 例えば以下のリストのように、Extendedクラスにある静的メソッドMethodは、あたかもClassクラスのメソッドのように呼び出すことができます。ポイントは、拡張メソッドのthisで修飾された引数(レシーバー)です。

リスト extended_method/Program.cs
public class Class
{
}

public static class Extended
{
    public static void Method(this Class c, string s)
    {
        Console.WriteLine(s);
    }
}

var c = new Class();
c.Method("Extended method");	// Extended method

 C# 14では、新しく導入されたextensionブロックにより、インスタンスメソッドに加えて、インスタンスプロパティや静的メソッド、静的プロパティが拡張メンバーとして定義できるようになりました。extensionブロックはメソッド・プロパティを置く静的クラスに、以下の構文で配置されます。

extension(レシーバー)
{
    プロパティ定義...
    メソッド定義...
}

 レシーバーは、従来の拡張メソッドと異なり、thisは不要です。このレシーバーを使ったプロパティ定義、メソッド定義をブロック内部に配置していきます。従来の書式において、レシーバーを一括指定するイメージです。

 以下のリストは、インスタンスメソッド、インスタンスプロパティを定義する例です。

リスト extended_member/Program.cs
public static class Extended
{
    private static int _field = 1;
    extension(Class c)	(1)
    {
        public void Method(string s)	(2)
        {
            Console.WriteLine(s);
        }

        public int Property { get => _field; }	(3)
    }    
}

 (1)のように、extensionブロックをレシーバーとともに宣言します。(2)はインスタンスメソッドの定義、(3)はインスタンスプロパティの定義です。それぞれ、通常のメソッド定義、プロパティ定義と変わらないことに注目してください。従来の拡張メソッドでは、静的メソッドとして定義する必要があったうえ、引数にレシーバーが必要でした。

 以下のリストは、静的メソッド、静的プロパティを定義する例です。

リスト extended_member/Program.cs
public static class Extended
{
    …略…
    extension(Class)	(1)
    {
        public static void StaticMethod(string s)	(2)
        {
            Console.WriteLine(s);
        }

        public static int StaticProperty { get => _field; }	(3)
    }
}

 静的メンバーの定義では、(1)のように、extensionブロックのレシーバーに型名のみ指定します。そして(2)(3)のように、static修飾子を付けてメンバーを定義します。

 ジェネリックな型についても拡張メンバーの利用が可能です。この場合、extension<T>というようにextensionブロック自体に型パラメーターを指定します。

リスト extended_member/Program.cs
public class GClass<T>
{
}

public static class Extended
{
    …略…
    extension<T>(GClass<T> t)
    {
        public T GenericMethod(T value) => value;
    }
}

var g = new GClass<int>();
Console.WriteLine(g.GenericMethod(42));	// 42

null条件代入(Null-conditional assignment)

 C# 14では、null条件メンバーアクセス演算子(?.)とnull条件要素アクセス演算子(?[])が、代入演算子または複合代入演算子の左辺で使用できるようになりました。

 C#には、null条件演算子がいくつかあり、本来であればif文を使うところを、省略されたコードでの記述を可能にしてくれます。

表 null条件演算子
演算子 概要
null合体演算子(??) a ?? b(aがnullならb、非nullならa)
null結合代入演算子(??=) a ??= b(aがnullならbを代入、非nullならaのまま)
null条件メンバーアクセス演算子(?.) a?.b(aがnullならnull、非nullならbにアクセス)
null条件要素アクセス演算子?[] a?[b](aがnullならnull、非nullならa[b]にアクセス)

 このうちnull条件メンバーアクセス演算子(?.)とnull条件要素アクセス演算子?[]は、代入演算子または複合代入演算子の左辺に現れる可能性があるにもかかわらず、使用できないという制限がありました。C# 14ではこれが緩和され、これらの演算子を代入演算子または複合代入演算子の左辺で利用できるようになりました。

リスト null_conditonal/Program.cs
class Customer
{
    public string? Name { get; set; }
}
…略…
var customer = (Customer)null;
customer?.Name = "Yamauchi";
Console.WriteLine(customer?.Name);	// null(何も出力されない)

customer = new Customer { Name = "Nao" };
customer?.Name = "Yamauchi";
Console.WriteLine(customer?.Name);	// Yamauchi

 customerがnullであれば何もせず、nullでなければ右辺の値をプロパティに代入します。なお、評価結果がnullの場合は、右辺自体が評価されないので、右辺が副作用のある式である場合には注意が必要です。また、if文で判定するときのような==演算子を使わないので、==演算子が副作用を持つ場合にも結果が異なってくる点にも注意が必要です。

 null条件要素アクセス演算子?[]も、代入式の左辺で利用できます。以下のように、?.演算子との組み合わせも可能です。

リスト null_conditonal/Program.cs
class Customer
{
    public int[] Items { get; set; }
}
…略…
customer?.Items?[0] = 100;
Console.WriteLine(customer?.Items?[0]);	// null(何も出力されない)

customer?.Items = new int[3];
customer?.Items?[0] = 100;
Console.WriteLine(customer?.Items?[0]);	// 100

 この場合、null条件メンバーアクセス演算子(?.)がまず評価され、次にnull条件要素アクセス演算子(?[])が評価されます(左結合)。

 null条件メンバーアクセス演算子とnull条件要素アクセス演算子は、複合代入演算子(+=、-=など)でも利用できます。これらと同じく値を書き換える演算子としてインクリメント・デクリメント演算子(++、--)がありますが、これらでは使用できません。

次のページ
バインドされていないジェネリック型のnameof(Unbound generic types and nameof)

この記事は参考になりましたか?

【C#で知っておくべき新機能】最新バージョンを徹底解説!連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 山内 直(WINGSプロジェクト ヤマウチ ナオ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/22718 2025/12/11 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング