VB.NETのイテレータパターン(C#はyieldを使用します!)[複製]
-
05-07-2019 - |
質問
この質問にはすでに回答があります:
- VB.NETでの収量 8つの答え
VB.NET にイテレータパターンを実装する方法 yield
キーワードですか?
解決
これは、VS 2010 SP1で非同期CTPでサポートされるようになりました。イテレータ( C#およびVisual Basic)on MSDN および Visual Studioをダウンロード非同期CTP(バージョン3)。
このようなコードは動作します:
Private Iterator Function SomeNumbers() As IEnumerable
' Use multiple yield statements.
Yield 3
Yield 5
Yield 8
End Function
他のヒント
VB.NETはカスタムイテレータの作成をサポートしていないため、C#yieldキーワードに相当するものはありません。ただし、ナレッジベースの記事 Visual Basic .NETまたはVisualの作成方法をご覧ください。詳細については、For Eachステートメントで使用可能な2005年の基本クラス 。
C#のyieldキーワードは、コンパイラがそれをサポートするためにバックグラウンドで状態マシンを作成することを強制します。 VB.Netにはyieldキーワードがありません。ただし、関数内にステートマシンを作成できる構造があります。静的関数メンバー。
必要なステートマシンと同様にIEnumerableを実装するジェネリッククラスを作成し、関数内に静的メンバーとしてインスタンスを配置することにより、yield return関数の効果を模倣することができるはずです。
もちろん、これは関数の外部でクラスを実装する必要があります。しかし、適切に行われれば、クラスは一般的な場合に再利用できるはずです。ただし、実装の詳細を提供するのに十分なアイデアで遊んでいません。
うーん、運が悪い:
今日、一部のC#をVB.NETに変換するときに問題に苦しんでいました。 C#には、非常に優れた「収益率」があります。列挙子オブジェクトに値を提供するために反復子ブロックで使用されるステートメント。 VB.NETには「歩留まり」がありません。キーワード。そのため、これを回避するためのいくつかのソリューションがあります(どれも本当にクリーンではありません)。ループ処理を行っており、列挙子を壊して単一の値を返したい場合、returnステートメントを使用して値を返すことができます。ただし、列挙全体を返したい場合は、子タイプのList()を作成してリストを返します。通常はIEnumerableでこれを使用しているため、List()はうまく機能します。
それは1年前に書かれたもので、それ以降誰かが他に何か良いものを思いついたかどうかはわかりません。
編集:これはVB.NETのバージョン11(VS2010以降)で可能になり、イテレーターのサポートが計画されています。仕様ここから入手できます。
LINQの式とメソッドの遅延実行プロパティと遅延評価プロパティにより、.NET 4.5でyieldステートメントが使用可能になるまで、カスタムイテレータを効果的に実装できることに注意してください。 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;
}
}