こんな Tuple が欲しかった的な(即興の落書き)

dynamic は便利な機能だけど、言語仕様に dynamic キーワードを持ち込まざるを得なかったのはちょっと残念だなと思う。むしろ、演算子でいいんじゃない?とか思わなくもない。

dynamic dx = any;

dx.Foo();
dx.Bar();

// 実際の DynamicObject には、public なコンストラクタはない
// 明示的変換やファクトリメソッドがあってもいいんじゃね?
var dx = new DynamicObject(any);

dx->Foo();
dx->Bar();

// パフォーマンスに影響があると思われるが、非 DynamicObject に対して呼びだしたら
// その場で暗黙的に DynamicObject に変換されてもいいんじゃない?と
any->Foo();

みたいにね...どうかな?インデクサあたりで問題になりそうだけどね...話題がそれているようで、一応前置きだったりします。
.NET4 の Tuple は dynamic と比べると「誰もが .NET 2.0/3.5 で作成していたであろうクラス」なレベルで収まっており、かなり残念な印象があった。*1 それなりの人が、anonymous type のような「動的な型」としての何かを望んでいたのではないだろうか?と思ったりする。ざっくり言うと、

  • Tuple 型は型パラメータの型情報だけでしか区別できないので new { Key="", Value="" } と new { Name="", Value="" } が、どちらも Tuple になってしまって区別できない。
  • Tuple 型が静的な型なので string Item1 が Key なのか Name なのか、それとも別の何かなのかをコードに表現しにくい。*2

のがダメだと思うんですよね。

// たとえば、こんな。
public tuple F(int n)
{
    return new { Number = n };
}

// 使うときは var で受ける(dynamic や object でもいいけどさ)
var x = F(1);
Console.WriteLine(x.Number);

// コンパイル後のイメージは、dynamic と同じく属性
[return: Tuple(Tuple.Create("Number"), typeof(int))]
public object F(int n)
{
    // 現実的には、コンパイラが生成する TupleAttribute には、
    // この返される匿名"型"を引数すれば十分
    return new { Number = n };
}

// もしくは Tuple<> を用いてメンバー名だけを属性に保持
[return: Tuple("Number")]
public Tuple<int> F(int n) { ... }
  • anonymous type は今まで通り作成する
  • その anonymous type が tuple として公開された場合、anonymous type の可視性をそれにあわせて protected/internal/public へと変更する
  • アセンブリ境界をまたぐ場合、公開された匿名型をそのまま使用しないで、使用する側のアセンブリに改めて匿名型として作成する。(それが再公開されない限りは private である)
  • 上記のアセンブリ境界をまたいだ受け渡しのため、匿名型同士の代入互換性を確保(匿名型のプロパティは読み取り専用なので、問題にはなりにくいだろう)

ぐらいの、dynamic の導入よりも少ない変更で実現しそうなのですけどねえ。

Tuple.Create(...) の挙動も現在の Tuple 型の仕様のままで一般形として使えると思うんですね。入力系は一般系に限定しちゃうことにして、

public IEnumerable<tuple> ToNameValue<T>(params Tuple<string, T>[] items)
{
    foreach (var item in items)
        yield return new { Name = item.Item1, Value = item.Item2 };
}

うーん、入力考えるといまいちイケてないな。

*1:品質が安定していて、開発者間で共有可能な実装や仕様が提供されることは十分に歓迎されることです...ライブラリによって比較方法やハッシュ値、参照型or値型とかがマチマチな 2.0/3.5 の世界はつらいです

*2:静的な Tuple<> 型よりも、具体性のある名前をもった小さいクラスが乱立する理由...*1 に対する逆行