このお話は、Visual Basic 中学校の管理人である、るきおさんとマイクロソフトの小高さんとお話していた時に
出た話です。ちょっとまとめてみたいと思います。
あまり言語仕様系については、得意ではないのですが、実際プログラムを作っていると、今後は意識しなければならない
点も多いような気がしたので、勉強のために掲載しておきます。
問題点は、C#でいう、yield return に相当するものがVBにはないため、LINQの拡張メソッドを作っても、
評価のタイミング(ようは、遅延評価)が、できないというものです。(即時実行になってしまうというもの。)
るきおさんが作った検証コード
◆ C#版 (yield return使用)
public partial class Form1 : Form
{ public Form1()
{ InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{ var names = new string[] { "エリマキトカゲ", "イノシシ", "アメンボ", "ウマ", "オオカミ" };
var query = names.FilteredLongHighPass().OrderBy(s=>s);
MessageBox.Show("これからLINQを評価します。"); MessageBox.Show(string.Join("\n", query.ToArray())); }
}
static class MyExtensions
{ public static IEnumerable<string> FilteredLongHighPass(this IEnumerable<string> values)
{ MessageBox.Show("拡張メソッドが呼ばれました!");
foreach (var value in values)
{ if (value.Length >= 3)
yield return value;
}
}
}
◆VB版
(yield returnは使用できないので、List(Of String) で代替えしたみたいです。
それにしても拡張メソッドを作るのに、Moduleが相変わらず気に入らないや)
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim names = New String() {"エリマキトカゲ", "イノシシ", "アメンボ", "ウマ", "オオカミ"}
Dim query = names.FilteredLongHighPass.OrderBy(Function(name) name)
MsgBox("これからLINQを評価します。")
MsgBox(String.Join(vbNewLine, query.ToArray()))
End Sub
End Class
Module Module1
<System.Runtime.CompilerServices.Extension()> _
Public Function FilteredLongHighPass(ByVal values As IEnumerable(Of String)) As IEnumerable(Of String)
MsgBox("拡張メソッドが呼ばれました!")
Dim results As New List(Of String)
For Each value In values
If Len(value) >= 3 Then
results.Add(value)
End If
Next
Return results
End Function
End Module
遅延評価のタイミングは抜きにしても、C#では、実体の型に関係なく、Collectionを返せるのに対し、
VBでは、それができない。(VBの場合は、IEnumerableインターフェースを適用しているクラスを返さなければならない)
これは、yield return がVBにないからという最大の弱点ですね。
そして、下記にあるコードが私が手を加えたコードです。(VB側の拡張メソッドの箇所のみ手を加えた)
Module ExtensionMethods
<System.Runtime.CompilerServices.Extension()> _
Public Function FilteredLongHighPass(ByVal values As IEnumerable(Of String)) As IEnumerable(Of String)
MsgBox("拡張メソッドが呼ばれました!")
Return values.Where(Function(value) value.Length >= 3)
End Function
End Module
ま、見てわかるとおり、LINQ + ラムダ式を使って、実態の型を意識しないで、IEnumerableインターフェースでCollectionを
返すようにできる方法ではありますが、うーん、これで良いのかな?
yield return には若干近づいたとは思うんですけど、Collectionは、実態の型か?というと、答えは、「No」だと私は思います。
遅延実行か?というと、「Yes」だと個人的には思いますが・・・どうなんだろ?間違っていたらご指摘ください。
さらに、
http://msdn.microsoft.com/ja-jp/library/bb943859.aspx にもあるように、
Visual Basic 9.0 には、yield キーワードに相当するものがありません。
このため、この言語での遅延実行の実装はより複雑になります。
とあるので、やはり、VBを使って、遅延評価・遅延実行させるには、類似したものは複雑だとは言え、実装可能ではあるものの
C#のyield return とまったく同じものができるかというと、厳密には「できない」というべきなのかもしれません。
この辺り、こうすると良いというお答えわかる方、いらっしゃいましたら、私やるきおさん、小高さんに情報をお寄せ下さい。
ぷはー、やっぱり、言語仕様系にめっぽう弱い、けろでした・・・(ましてや、ラムダ式は更に苦手です・・・)