2016/09/24 (土) の朝、Expression-Bodied Everything (= どこでもラムダっぽく) に関する Pull-Request が Roslyn の master ブランチにマージされました!
C# 6 ではプロパティとメソッドの 2 箇所についてラムダ形式のメンバーを記述することができましたが、それをさらに拡張していろんなところで使えるようにするというものです。
C# 6 の頃から「ラムダ形式」と書いているのでその名残でタイトルを付けましたが、「式形式 / 式っぽい / 式の体をした」と言うのかもしれません。ここら辺は日本語訳が正式には決まってないと思うので結構テキトーです...。
新たに使えるようになる場所
C# 7 から増えたもの (= 2016/09/27 時点で動作するもの) は以下の 4 つです。
- コンストラクタ
- デストラクタ
- プロパティのアクセサ
- インデクサ
例えば以下のように書けます。
class Program
{
public string Property
{
get => "Getter Property";
set => Console.WriteLine(value);
}
public string this[int index]
{
get => $"{index} : Getter Indexer";
set => Console.WriteLine($"{index} : {value}");
}
public Program(string text) => Console.WriteLine(text);
~Program() => Console.WriteLine("Destructor");
static void Main()
{
var x = new Program("Constructor");
x.Property = "Setter Property";
Console.WriteLine(x.Property);
x[123] = "Setter Indexer";
Console.WriteLine(x[123]);
}
}
逆コンパイル
先の例を逆コンパイルしてみると以下のようになります。お分かりの通り、これまでの書き方をより簡易にするための糖衣構文でしかありません。
class Program
{
public string Property
{
get { return "Getter Property"; }
set { Console.WriteLine(value); }
}
public string this[int index]
{
get { return string.Format("{0} : Getter Indexer", index); }
set { Console.WriteLine(string.Format("{0} : {1}", index, value)); }
}
public Program(string text)
{
Console.WriteLine(text);
}
~Program()
{
Console.WriteLine("Destructor");
}
private static void Main()
{
Program x = new Program("Constructor");
x.Property = "Setter Property";
Console.WriteLine(x.Property);
x[123] = "Setter Indexer";
Console.WriteLine(x[123]);
}
}
[おまけ] イベントへの対応
プロパティの get
/set
ができるのなら、(滅多に使われない) イベントの add
/remove
もできるのでは?と思った方もいらっしゃるかもしれません。ただ、残念ながら執筆時点 (2016/09/27) では csc.exe がクラッシュして死にます。Roslyn の実装を見ると add
/remove
にも対応してそうな雰囲気を感じるところもあるのですが...。
private EventHandler click;
public event EventHandler Click
{
add => this.click += value;
remove => this.click -= value;
}
イベントビューアで確認するとスタックトレースはこんな感じで吐かれていました。
アプリケーション:csc.exe
フレームワークのバージョン:v4.0.30319
説明: ハンドルされない例外のため、プロセスが中止されました。
例外情報:System.InvalidOperationException
場所 Microsoft.Cci.ReferenceIndexer.ProcessMethodBody(Microsoft.Cci.IMethodDefinition)
場所 Microsoft.Cci.MetadataVisitor.Visit(Microsoft.Cci.ITypeDefinitionMember)
場所 Microsoft.Cci.MetadataVisitor.Visit(System.Collections.Generic.IEnumerable`1<Microsoft.Cci.IMethodDefinition>)
場所 Microsoft.Cci.ReferenceIndexerBase.Visit(Microsoft.Cci.ITypeDefinition)
場所 Microsoft.Cci.MetadataVisitor.Visit(System.Collections.Generic.IEnumerable`1<Microsoft.Cci.INamedTypeDefinition>)
場所 Microsoft.Cci.ReferenceIndexer.Visit(Microsoft.CodeAnalysis.Emit.CommonPEModuleBuilder)
場所 Microsoft.Cci.MetadataWriter.CreateIndices()
場所 Microsoft.Cci.MetadataWriter.BuildMetadataAndIL(Microsoft.Cci.PdbWriter, System.Reflection.Metadata.BlobBuilder, System.Reflection.Metadata.BlobBuilder, System.Reflection.Metadata.BlobBuilder, System.Reflection.Metadata.Blob ByRef, System.Reflection.Metadata.Blob ByRef)
場所 Microsoft.Cci.PeWriter.WritePeToStream(Microsoft.CodeAnalysis.Emit.EmitContext, Microsoft.CodeAnalysis.CommonMessageProvider, System.Func`1<System.IO.Stream>, System.Func`1<System.IO.Stream>, Microsoft.Cci.PdbWriter, System.String, Boolean, Boolean, System.Threading.CancellationToken)
場所 Microsoft.CodeAnalysis.Compilation.SerializeToPeStream(Microsoft.CodeAnalysis.Emit.CommonPEModuleBuilder, EmitStreamProvider, EmitStreamProvider, System.Func`1<System.Object>, Microsoft.CodeAnalysis.DiagnosticBag, Boolean, System.Threading.CancellationToken)
場所 Microsoft.CodeAnalysis.CommonCompiler.RunCore(System.IO.TextWriter, Microsoft.CodeAnalysis.ErrorLogger, System.Threading.CancellationToken)
場所 Microsoft.CodeAnalysis.CommonCompiler.Run(System.IO.TextWriter, System.Threading.CancellationToken)
場所 Microsoft.CodeAnalysis.CSharp.CommandLine.Csc+<>c__DisplayClass1_0.<Run>b__0(System.IO.TextWriter)
場所 Microsoft.CodeAnalysis.CommandLine.ConsoleUtil.RunWithUtf8Output[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]](System.Func`2<System.IO.TextWriter,Int32>)
場所 Microsoft.CodeAnalysis.CSharp.CommandLine.Csc.Run(System.String[], Microsoft.CodeAnalysis.CommandLine.BuildPaths, System.IO.TextWriter, Microsoft.CodeAnalysis.IAnalyzerAssemblyLoader)
場所 Microsoft.CodeAnalysis.CommandLine.DesktopBuildClient.RunLocalCompilation(System.String[], Microsoft.CodeAnalysis.CommandLine.BuildPaths, System.IO.TextWriter)
場所 Microsoft.CodeAnalysis.CommandLine.BuildClient.RunCompilation(System.Collections.Generic.IEnumerable`1<System.String>, Microsoft.CodeAnalysis.CommandLine.BuildPaths, System.IO.TextWriter)
場所 Microsoft.CodeAnalysis.CommandLine.DesktopBuildClient.Run(System.Collections.Generic.IEnumerable`1<System.String>, System.Collections.Generic.IEnumerable`1<System.String>, Microsoft.CodeAnalysis.CommandLine.RequestLanguage, Microsoft.CodeAnalysis.CommandLine.CompileFunc, Microsoft.CodeAnalysis.IAnalyzerAssemblyLoader)
場所 Microsoft.CodeAnalysis.CSharp.CommandLine.Program.Main(System.String[], System.String[])
場所 Microsoft.CodeAnalysis.CSharp.CommandLine.Program.Main(System.String[])
Expression-Bodied なイベントへの対応も提案としては出ているので、今後 (どういった書き味になるかはさておき) 対応が入るかもしれませんね。