BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース C# 7.1 の早期情報: Part 2

C# 7.1 の早期情報: Part 2

原文(投稿日:2017/06/06)へのリンク

昨日、我々は非同期 Main とデフォルト式を取り上げた。C# 7.1 の案内は、タプル名の推測、およびジェネリクスを用いたパターンマッチング、というタイトルの提案によって続く。

タプル名の推測

開発者たちは頻繁には意識しないことだが、C# の匿名型は名前推測を含む。例えば、以下のように書けばオブジェクト y は A と B という名前のプロパティを持つだろう。

var y = new { x.A, x.B };

タプル名の推測の提案では、値タプルはおよそ同じ能力を持つようになる。

var z1 = (A: x.A, B: x.B); //explicit names
var z2 = (x.A, x.B); //inferred names

匿名型と値タプルの間にはいくつかの顕著な違いがある。

  • 匿名型はプロパティ名が必要である。明示的であるか、推測されたものであるかにかかわらず。
  • 値タプルは名前付けられていないプロパティに Item 1 や Item 2 などのラベルを付与する。
  • 名前が重複した匿名型はコンパイルエラーとなる。
  • 明示的に名前を重複させた値タプルはコンパイルエラーとなる。
  • 推測されて名前が重複した値タプルは、名前推測がスキップされる。たとえば、 (x.A, x.B, y.A) は (Item1, B, Item3) となる。
  • 値タプルは ToString, Rest, ItemN (N > 0) という予約された名前を使用できない。

C# と VB の興味深い違いは、 VB は匿名プロパティ名を関数から推測できるということだ。例えば、

var y = new { x.A, x.Bar() }; //compiler error
Dim y = New With {x.A, x.Bar()} //anonymous type {A,Bar}

この能力は VB においてタプルに拡張されるだろう。

もし推測されたプロパティと同じ名前で拡張メソッドを使用していた場合、この機能は破壊的変更となる可能性がある。提案は続ける。

互換性の会議では、この破壊は許容可能であると判断しました。限定的であることに加えタプルがリリースされてから (C# 7.0 において) の時間窓が短いためです。

ジェネリック制約でのタプル名

また、コンパイラーがプログラマーにタプル名の不一致を警告することも試行されている。例えば、

public static (int A, int B) Test1((int A, int B) a)
Test1((A: 1, B: 2));
Test1((X: 1, Y: 2)); //warning, tuple name mismatch

これはジェネリック制約に入れてしまえば発生しない。

public static T Test2<T>(T a) where T : IEnumerable<(int A, int B)>
Test2(new List<(int A, int B)>());
Test2(new List<(int X, int Y)>()); //no warning

すなわち、ジェネリック制約のケースでのタプル名はコンパイラーにチェックされないということだ。理論的にはコンパイラーはこうした場合に対応可能だが、パフォーマンスのコストが利益に対して大きすぎる。

ジェネリクスによるパターンマッチング

C# 7.0 で登場したパターンマッチングは、ジェネリクスについて設計上の欠点がある。Alex Wiese 氏によって送られたコードを考える。

class Program
{
    static void Main(string[] args) {}

    public void Send<T>(T packet) where T : Packet
    {
        if (packet is KeepalivePacket keepalive)
        {
            // Do stuff with keepalive
        }
        
        switch (packet)
        {
            case KeepalivePacket keepalivePacket:
                // Do stuff with keepalivePacket
                break;
        }
    }
}

public class Packet {}

public class KeepalivePacket : Packet {}

このコードは「型 T の式が KeepalivePacket 型のパターンでハンドルできません。」というエラーを返す。しかし、パラメーターを T の代わりに System.Object にすると、期待どおりに動作する。

public void Send(object packet)

C# 7.1 では、パターンマッチングが発生する際のルールを少し変更することで、これが修正される。

パターンマッチングの仕様の段落を変更する(提案による追加は太字で表す)。

左側の静的な型と与えられた型の特定の組み合わせでは互換性が無くコンパイル時エラーとなると考えられます。静的な型 E の値は型 T に対して、 E から T への同一性変換、暗黙的参照変換、ボクシング変換、明示的参照変換、非ボクシング変換のいずれかがある、または、 E あるいは T がオープンな型であれば、パターン互換であると言えます。型 E の式が、一致する型パターンの型にパターン互換でない場合、コンパイル時エラーとなります。

これはバグフィックスと考えられるが、これにより “前方非互換性が生じる” ことから、変更の恩恵を受けるためにはコンパイラーを C# 7.1 に更新する必要があるだろう。

 
 

Rate this Article

Adoption Stage
Style
 
 

この記事に星をつける

おすすめ度
スタイル

BT