C#8 GA後のswitch式を見てみる

GAのタイミングで再確認してみるswitch式

後置き構文で読みやすいswitch式ですが、プレビュー時代は、以下の書き方をする必要がありました。

            static IEnvironmentBuilder GetEnvironmentBuilder(string environmentName) {
                return environmentName switch
                {
                    DefaultEnvironmentNames.Development => (IEnvironmentBuilder)new DevelopmentBuilder(),
                    DefaultEnvironmentNames.DevelopmentRemote => new DevelopmentRemoteBuilder(),
                    DefaultEnvironmentNames.Staging => new StagingBuilder(),
                    DefaultEnvironmentNames.Production => new ProductionBuilder(),
                    _ => throw new System.ArgumentException($"{nameof(environmentName)}")
                };
            }

なぜか推論出来ない問題(@プレビュー時代)

return してるので普通に考えると、IEnvironmentBuilder を返却する式であることは推論できるはずなんですが、1番目で、 (IEnvironmentBuilder)とキャストして上げないと、コード解析で赤波線が出ていました。(逆に最初に書きさえすれば、2番目以降はIEnvironmentBuilder を実装したオブジェクトを設定してあげていれば大丈夫でした。)

なんか不格好だなーと思っていたんですが、.NET Core 3対応を行っていくタイミングで、このキャスト部分がコード解析で、「冗長だぞ!」って言われるようになっていることに気づきました。

f:id:CreatioVitae:20191017161954p:plain

return してるときは、戻り値から推論してくれるように改善されているようです。なので今は、

            static IEnvironmentBuilder GetEnvironmentBuilder(string environmentName) {
                return environmentName switch
                {
                    DefaultEnvironmentNames.Development => new DevelopmentBuilder(),
                    DefaultEnvironmentNames.DevelopmentRemote => new DevelopmentRemoteBuilder(),
                    DefaultEnvironmentNames.Staging => new StagingBuilder(),
                    DefaultEnvironmentNames.Production => new ProductionBuilder(),
                    _ => throw new System.ArgumentException($"{nameof(environmentName)}")
                };
            }

で大丈夫。

varで受ける場合

varで受ける場合は、いつもの型推論の制約で、抽象的な型で受けられません。なのでこうなります。

f:id:CreatioVitae:20191017163043p:plain

なので、varで受け取る場合に、抽象的な型で受け取りたい場合はキャストが結局必要です。

            static IEnvironmentBuilder GetEnvironmentBuilder(string environmentName) {

                var environmentBuilder = environmentName switch
                {
                    DefaultEnvironmentNames.Development => (IEnvironmentBuilder) new DevelopmentBuilder(),
                    DefaultEnvironmentNames.DevelopmentRemote => new DevelopmentRemoteBuilder(),
                    DefaultEnvironmentNames.Staging => new StagingBuilder(),
                    DefaultEnvironmentNames.Production => new ProductionBuilder(),
                    _ => throw new System.ArgumentException($"{nameof(environmentName)}")
                };

                return environmentBuilder;
            }

ローカル変数で受ける場合

varで受ける場合~の話と、推論の話で、もう予想はつくと思いますが、ローカル変数でswitch式を受ける場合はキャストは不要です。

            static IEnvironmentBuilder GetEnvironmentBuilder(string environmentName) {
                IEnvironmentBuilder environmentBuilder;
                environmentBuilder = environmentName switch
                {
                    DefaultEnvironmentNames.Development => new DevelopmentBuilder(),
                    DefaultEnvironmentNames.DevelopmentRemote => new DevelopmentRemoteBuilder(),
                    DefaultEnvironmentNames.Staging => new StagingBuilder(),
                    DefaultEnvironmentNames.Production => new ProductionBuilder(),
                    _ => throw new System.ArgumentException($"{nameof(environmentName)}")
                };

                return environmentBuilder;
            }

switch式、後置き構文なこともあって、switch ステートメントよりもずいぶん読みやすくなりました。 使える時には積極的に使っていきましょう。