キーワードを持たないVB.NETで反復子パターンをどのように実装しますか?yield
6 に答える
これは、VS 2010 SP1 で Async CTP を使用してサポートされるようになりました。MSDN の Iterators (C# and Visual Basic) を参照し、Visual Studio Async CTP (Version 3)をダウンロードしてください。
このようなコードは動作します:
Private Iterator Function SomeNumbers() As IEnumerable
' Use multiple yield statements.
Yield 3
Yield 5
Yield 8
End Function
VB.NET はカスタム イテレータの作成をサポートしていないため、C# の yield キーワードに相当するものはありません。ただし、詳細については、KB 記事「Visual Basic .NET または Visual Basic 2005 クラスを For Each ステートメントで使用できるようにする方法」を参照してください。
C# の yield キーワードは、コンパイラがサポートするためにバックグラウンドでステート マシンを作成するように強制します。VB.Net には yield キーワードがありません。ただし、関数内にステート マシンを作成できるようにする構造体があります: Static function members。
必要なステート マシンだけでなく IEnumerable を実装するジェネリック クラスを作成し、インスタンスを静的メンバーとして関数内に配置することで、yield リターン関数の効果を模倣できるはずです。
もちろん、これには関数の外部でクラスを実装する必要があります。しかし、適切に行われれば、クラスは一般的なケースで再利用可能になるはずです。ただし、実装の詳細を提供するのに十分なほどアイデアを試していません。
うーん、あなたは運が悪いようです:
今日、一部の C# を VB.NET に変換するときに問題が発生しました。C# には、列挙子オブジェクトに値を提供するために反復子ブロックで使用される非常に優れた "yield return" ステートメントがあります。VB.NET には「yield」キーワードがありません。したがって、これを回避するための解決策がいくつかあります (どれも本当にクリーンではありません)。ループ処理中に列挙子を中断して単一の値を返したい場合は、return ステートメントを使用して値を返すことができます。ただし、列挙全体を返したい場合は、子型の List() を作成し、リストを返します。通常、これは IEnumerable で使用するため、List() は適切に機能します。
これは 1 年前に書かれたものです。
編集: これは VB.NET のバージョン 11 (VS2010 以降のもの) で可能になり、イテレータのサポートが計画されています。仕様はこちらから入手できます。
.NET 4.5 で yield ステートメントが使用可能になるまで、LINQ 式とメソッドの遅延実行と遅延評価のプロパティにより、カスタム反復子を効果的に実装できることに注意してください。Yield は、LINQ の式とメソッドによって内部的に使用されます。
次のコードはこれを示しています。
Private Sub AddOrRemoveUsersFromRoles(procName As String,
applicationId As Integer,
userNames As String(),
rolenames As String())
Dim sqldb As SqlDatabase = CType(db, SqlDatabase)
Dim command As DbCommand = sqldb.GetStoredProcCommand(procName)
Dim record As New SqlDataRecord({New SqlMetaData("value", SqlDbType.VarChar,200)})
Dim setRecord As Func(Of String, SqlDataRecord) =
Function(value As String)
record.SetString(0, value)
Return record
End Function
Dim userNameRecords As IEnumerable(Of SqlDataRecord) = userNames.Select(setRecord)
Dim roleNameRecords As IEnumerable(Of SqlDataRecord) = rolenames.Select(setRecord)
With sqldb
.AddInParameter(command, "userNames", SqlDbType.Structured, userNameRecords)
.AddInParameter(command, "roleNames", SqlDbType.Structured, roleNameRecords)
.AddInParameter(command, "applicationId", DbType.Int32, applicationId)
.AddInParameter(command, "currentUserName", DbType.String, GetUpdatingUserName)
.ExecuteNonQuery(command)
End With
End Sub
以下は出力を与えます: 2, 4, 8, 16, 32
VB.NET で
Public Shared Function setofNumbers() As Integer()
Dim counter As Integer = 0
Dim results As New List(Of Integer)
Dim result As Integer = 1
While counter < 5
result = result * 2
results.Add(result)
counter += 1
End While
Return results.ToArray()
End Function
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
For Each i As Integer In setofNumbers()
MessageBox.Show(i)
Next
End Sub
C# の場合
private void Form1_Load(object sender, EventArgs e)
{
foreach (int i in setofNumbers())
{
MessageBox.Show(i.ToString());
}
}
public static IEnumerable<int> setofNumbers()
{
int counter=0;
//List<int> results = new List<int>();
int result=1;
while (counter < 5)
{
result = result * 2;
counter += 1;
yield return result;
}
}