Как реализовать шаблон итератора в VB.NET, в котором нет ключевого слова yield
?
Шаблон итератора в VB.NET (C # будет использовать yield!)
Ответы (6)
Теперь это поддерживается в VS 2010 SP1 с Async CTP, см. Итераторы (C # и Visual Basic) в MSDN и загрузить Visual Studio Async CTP ( Версия 3).
Такой код работает:
Private Iterator Function SomeNumbers() As IEnumerable
' Use multiple yield statements.
Yield 3
Yield 5
Yield 8
End Function
VB.NET не поддерживает создание настраиваемых итераторов и, следовательно, не имеет эквивалента ключевому слову yield в C #. Однако вы можете прочитать статью базы знаний Как сделать Visual Basic .NET или Visual Базовый класс 2005, который можно использовать в инструкции For Each для получения дополнительной информации.
Ключевое слово C # yield заставляет компилятор создавать конечный автомат в фоновом режиме для его поддержки. VB.Net не имеет ключевого слова yield. Но у него есть конструкция, которая позволит вам создать конечный автомат внутри функции: Статические функции-члены.
Должна быть возможность имитировать эффекты возвращаемой функции yield путем создания универсального класса, который реализует IEnumerable, а также необходимого конечного автомата, и размещения экземпляра в качестве статического члена внутри вашей функции.
Это, конечно, потребует реализации класса вне функции. Но если все сделано правильно, в общем случае класс можно будет использовать повторно. Однако я недостаточно поиграл с этой идеей, чтобы предоставить какие-либо подробности реализации.
Хм, похоже, вы не повезло:
Сегодня я боролся с проблемой при преобразовании некоторых C # в VB.NET. В C # есть действительно классный оператор yield return, который используется в блоке итератора для предоставления значения объекту перечислителя. VB.NET не имеет ключевого слова yield. Итак, есть несколько решений (ни одно из которых не является действительно чистым), чтобы обойти это. Вы можете использовать оператор return для возврата значения, если вы выполняете цикл и хотите прервать работу перечислителя и вернуть одно значение. Однако, если вы хотите вернуть все перечисление, создайте List () дочернего типа и верните список. Поскольку вы обычно используете это с IEnumerable, List () будет работать нормально.
Это было написано год назад, я не уверен, что с тех пор кто-нибудь придумал что-нибудь лучше.
Изменить: это будет возможно в версии 11 VB.NET (после VS2010), планируется поддержка итераторов. Спецификация доступна здесь.
Имейте в виду, что свойства отложенного выполнения и отложенного вычисления выражений и методов LINQ позволяют нам эффективно реализовывать настраиваемые итераторы до тех пор, пока оператор yield не станет доступен в .NET 4.5. 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
In 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;
}
}