2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

LINQ使ったら勝手にインデクサが生える

Last updated at Posted at 2020-01-29

ElementAtOrDefaultは勝手にインデクサになるよ。

日々レガシーコードと戦いつつ、やっとLINQが普及しはじめたかという弊社。
先日レビューでこのようなコードに出くわしました。
For Each使えとかは言わないでください。

' stringsは適当な文字列コレクション。
Dim sorted = strings.OrderBy(Function(s) s.Length)

For i = 0 To sorted.Count - 1

    Dim item = sorted(i)
    ' 略

Next

初見で「HAHAHA、IOrderdEnumerableにDefaultプロパティは無いよ。せめてコンパイルの通るコードを出してくれたまえ。」と突き返そうとしました。
ところが聞くと、コンパイルどころかテストも通っているというではないですか。

この手の「なぜか動いてしまうVBコード」と戦うのも慣れたもので、まさかと思いつつ言語仕様に当たったところ、ありました。
Visual Basic 言語仕様 | 式 | クエリ式

既定のクエリインデクサー
要素型が T で、既定のプロパティを持たないすべてのクエリ可能なコレクション型は、次の一般的な形式の既定のプロパティを持つと見なされます。

Public ReadOnly Default Property Item(index As Integer) As T
Get
Return Me.ElementAtOrDefault(index)
End Get
End Property


この仕様によって、すべてのIEnumerable(Of T)はElementAtOrDefaultをインデクサとみなしてアクセスできます。やったね!

いや$O(n)$のインデクサって。[^1] 場合によって副作用もありって。

LINQ慣れした皆さんならお察しの通り、先のコードはOrderByの遅延実行のため$n$によって指数的に重くなります。
いっそコンパイルエラーを出してくれた方がよい気もするのですが、既にリリースされてしまっている仕様なので諦めて注意しましょう。

# 余談
この仕様はいわゆるダックタイピングとなっており、特定のインターフェースを要求しません。
要求されているのは以下の条件です。

- クエリ可能な型であること。(詳細は省略。)
- 既定の(Default)プロパティを持たないこと。
- Int32を引数とするElementAtOrDefaultメソッドを持つこと。

最小実装としてはこんな感じです。

```vb
Class Indexable(Of T)

    Function [Select](Of T, TResult)(selector As Func(Of T, TResult)) As Indexable(Of TResult)
    End Function

    Function ElementAtOrDefault(index As Int32) As T
    End Function

End Class

面白いのは拡張メソッドでもインデクサにできることでしょうか。おかげでIEnumerable(Of T)にも使えますし。
これで任意の型にインデクサが生やせます。やったね!

……この仕様、要る?

2
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?