196
132

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C# 小ネタ:.NET 5.0 で使える C# 9.0 で気に入ってる機能紹介

Last updated at Posted at 2020-11-07

C# 9.0 がリリースされるまで秒読みになりましたね!前に C# 8.0 風書き方アレコレを書いてみましたが、9.0 になってしまうと、また新機能が追加されてより良い言語になっていきます。

C# 小ネタ:C# 8.0 風の書き方アレコレ

C# 9.0 は .NET 5.0 で使えるので LTS 版じゃないので仕事で本格的に使える人は少ないかもしれませんが LTS の .NET 6.0 に計画的に移行するように考えて使うというのはありかなぁと思います。
まぁ、そこらへんは置いといて新しいものがでたら趣味では試したい!!ということでやってみましょう。

レコード型

多分、今回追加された中で個人的に一番使いそうな機能です。
イミュータブルなクラスを定義するのが凄く簡単になります。

X と Y というプロパティを持った Point 型は以下のように定義できます。

record Point(int X, int Y);

ちょっと使ってみましょう。

using System;

namespace ConsoleApp18
{
    class Program
    {
        static void Main(string[] args)
        {
            // クラスと同じ感じで new して使って
            var p1 = new Point(10, 20);
            // プロパティにアクセスできて
            Console.WriteLine($"{p1.X}, {p1.Y}");
            // ToString もいい感じに表示される
            Console.WriteLine(p1.ToString());
        }
    }

    record Point(int X, int Y);
}

実行するとこんな感じに表示されます。

10, 20
Point { X = 10, Y = 20 }

特徴の 1 つはプロパティは読み取り専用で書き換え不可能な点です。

var p1 = new Point(10, 20);
p1.X = 100; // エラー

一部分だけ値を書き換えたいときは with 式という、これもまた新しい機能で一部分だけ書き換えたコピーを簡単に作れます。他にも分解や比較も出来ます。

// クラスと同じ感じで new して使って
var p1 = new Point(10, 20);
// X を 100 にしたコピーを作成
var p2 = p1 with { X = 100 };

// 分解も対応
var (x, y) = p1;

// プロパティ同士の値を使って比較もしてくれる
if (p1 == p2)
{
    
}

レコード型は自分でプロパティを定義したりメソッドも定義できます。

record Point
{
    public int X { get; init; }
    public int Y { get; init; }

    public void Show() => Console.WriteLine($"({X}, {Y})");
}

プロパティに set のかわりに init をつけることでオブジェクト初期化子だけで設定可能なプロパティも指定できます。これはレコードだけじゃなくてクラスや構造体でもいけます。

// OK
var p1 = new Point { X = 100, Y = 200 };
// これはエラー
p1.X = 200;
// プロパティの値を変えたものが欲しい場合は上で紹介したのと同じで with でコピーを作る
var p2 = p1 with { X = 999 };

継承も OK

record Point(int X, int Y);
record Point3D(int X, int Y, int Z) : Point(X, Y);

レコードはいいですね。いい。

トップレベルステートメント

これはコンソールアプリとかの Main メソッドを定義しなくてもいい感じにやってくれる奴。こいつもいいやつ。
これを使うとハローワールドがこうなる。最高

using System;
Console.WriteLine("Hello world");

ASP.NET Core のハローワールド返すだけの Web アプリのコードとかもこんなにすっきりします。

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(builder =>
        builder.Configure(app =>
        {
            app.UseRouting();
            app.UseEndpoints(
                endpoints => endpoints.MapGet(
                    "/", 
                    context => context.Response.WriteAsync("Hello world"))
            );
        })
    ).Build().Run();

null じゃないんですよを簡単に書ける

シンプルだけど可読性という点ではいい。is not null って書ける。

using System;

object o = null;

// 古き良き書き方
if (o != null)
{
    // null じゃない
}

// C# 8.0 だけとこう書けるけど知らないとわかりにくい
if (o is { })
{
    // null じゃない
}

// C# 9.0
if (o is not null)
{
    // null じゃない
}

大なり小なりの比較も簡単に

これは、もしかしたら賛否ありそう。でも、予備知識なしだと新しい書き方のほうが読みやすいだろうなぁ。慣れてる人はそうでもないかも。

using System;

var x = 15;

// 今まで
if (x >= 10 && x <= 20)
{
    Console.WriteLine("10 以上で20 以下");
}

// C# 9.0
if (x is >= 10 and <= 20)
{
    Console.WriteLine("10 以上で20 以下");
}

// 今まで
if (x >= 10 && x <= 20 || x >= 100)
{
    Console.WriteLine("10 以上で20 以下もしくは 100 以上");
}

// C# 9.0
if (x is >= 10 and <= 20 or >= 100)
{
    Console.WriteLine("10 以上で20 以下もしくは 100 以上");
}

追記

@albireo さんがコメントで以下のコード例を書いてくれました。確かに…!

新しい大なり小なりは、比較対象が長い名前や深いプロパティ階層になってしまうときにありがたみが実感できそうです。

//長い名前だとよく似た別のプロパティをうっかり比較してても気づきにくい
if (datagridview.CurrentCellAddress.X >= 0 && datagridview.CurrentCellAddress.Y <= 10) { }

//それを防ぐためにローカル変数を使うのもすっきりしない
var currentX = datagridview.CurrentCellAddress.X;
if (curretnX >= 0 && currentX <= 10) { }

//すっきり
if (datagridview.CurrentCellAddress.X is >= 0 and <= 10) { }



//メソッドの返り値が範囲内かどうかを知りたいだけなのにいったんローカル変数に格納
var result = func();
if (result >= 0 && result <= 10) { }

//これだとメソッドを二回実行してしまう
if (func() >= 0 && func() <= 10) { }

//返り値の範囲チェックだけならこれでOK
if (func() is >= 0 and <= 10) { }

Target-typed new 式

日本語の正しい訳がわからない…けど型が明らかな時には new 演算子で型名を省略できる便利機能。

例えば…

// 右辺に型が書いてるので省略できる
Person p = new();
// ↑は個人的には var かも
var p = new Person();

// メソッドの戻り値の型はわかってるので省略できる
record Person(string Name);

Person foo()
{
    // なんか処理
    return new("Tanak");
}

// メソッドの引数の型はわかってるので省略できる
record Person(string Name);
void foo(Person p)
{
    // なんか処理
}

foo(new("Tanaka")); // PropertyChangedEventArgs の new とかで使いそう

まとめ

C# 9.0 の新機能をまとめた公式ドキュメントは以下のページにあります。

C# 9.0 の新機能

まだ、新しいページなので機械翻訳だから残念な部分もあるので読める人は英語で目を通すことをお勧めします。そのうち人による翻訳になるといいなぁ…

公式じゃないところだと安定の岩永さんのサイトもお勧め。まだ網羅はされてないみたいだけど、そのうち…?

C# 言語自体の機能じゃないけどソースジェネレーターというソースコード生成の仕組みも入ったので、みんな大好き Excel VBA マクロでソースコード吐き出してる人は移行先としていいかも??

ということで、来週のリリースが楽しみですね!!

196
132
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
196
132

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?