C#
.NET

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から変更が入ったそうです。これについては、こちらのツイートから辿れる会話もぜひ確認してみてください。

https://twitter.com/RyotaMurohoshi/status/1107489529043148800

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


余談

レコードが欲しい・・・

https://github.com/dotnet/roslyn/blob/master/docs/features/records.md


参考リンク

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