C#の分解と式形式のコンストラクタの組み合わせ


C# 7.0ではこんなコードが書けます。

public class Person
{
    public string FirstName { get; }
    public string LastName { get; }

    public Person(string firstName, string lastName)
        => (FirstName, LastName) = (firstName, lastName);
}

これは、C# 7.0から入った

を使ったコードです。

これを使わないコードだと、こうなります。

public Person {
    public string FirstName { get; }
    public string LastName { get; }

    public Person(string firstName, string lastName)
    {
        this.FirstName = firstName;
        this.LastName = lastName;
    }
}

分解式形式のコンストラクタを使っても、そんなに可読性は変わらないと思うので、お好みで。

ただ、MSの公式ドキュメントに使ったコード例があるので、読めるようにはしておいたほうがいいでしょう。

ちなみに、C# 6.0から入ったゲッターオンリーのプロパティーも使っています。これを使わないとしたらだいたいこんな感じに。

public class Person
{
    public string FirstName { get { return firstName; } }
    private readonly string firstName;

    public string LastName { get { return lastName; } }
    private readonly string lastName;

    public PersonOld(string firstName, string lastName)
    {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

内部的にValueTupleは作られない

「分解」と「式形式のコンストラクタ」を使ったコードては、一見ValueTupleを生成しているように読めます。

using System;

public class Person
{
    public string FirstName { get; }
    public string LastName { get; }

    public Person(string firstName, string lastName)
        => (FirstName, LastName) = (firstName, lastName);
}

SharpLabを使って、C# => IL => DecompileしたC#のコードは次の通り。

コードのコンストラクタを見ると、ValueTupleが作成されていません。

using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
public class Person
{
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly string <FirstName>k__BackingField;

    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly string <LastName>k__BackingField;

    public string FirstName
    {
        [CompilerGenerated]
        get
        {
            return <FirstName>k__BackingField;
        }
    }

    public string LastName
    {
        [CompilerGenerated]
        get
        {
            return <LastName>k__BackingField;
        }
    }

    public Person(string firstName, string lastName)
    {
        <FirstName>k__BackingField = firstName;
        <LastName>k__BackingField = lastName;
    }
}

これはC# 7.1から変更が入ったそうです。これについては、こちらのツイートから辿れる会話もぜひ確認してみてください。

ありがとうございました。

余談

レコードが欲しい・・・

参考リンク

GitHubの関連ありそうなissueやドキュメントはこちら。