关于.net:VB.NET中的收益

关于.net:VB.NET中的收益

Yield in VB.NET

C#具有名为yield的关键字。 VB.NET缺少此关键字。 Visual Basic程序员如何解决缺少此关键字的问题? 他们实现自己的迭代器类吗? 还是他们尝试编写代码以避免需要迭代器?

yield关键字确实会强制编译器在后台进行一些编码。 在C#中实现迭代器及其结果(第1部分)就是一个很好的例子。


注意:这个答案现在已经很旧了。此后,迭代器块已添加到VB.NET

C#在编译时将yield关键字转换为状态机。 VB.NET没有yield关键字,但是它有自己的机制可以安全地将状态嵌入到C#中不易使用的函数中。

通常使用Shared关键字将C#static关键字转换为Visual Basic,但是在两个地方会引起混淆。一个是C#静态类实际上是Visual Basic中的模块,而不是Shared类(您可能会认为,它们可以在Visual Basic中以任何一种方式进行编码,但是没有)。另一个是VB.NET确实有其自己的static关键字。但是,static在VB.NET中具有不同的含义。

您可以在VB.NET中使用static关键字在函数内部声明变量,然后在执行函数调用时,变量将保留其状态。这与仅在C#中声明私有静态类成员不同,这是因为VB.NET中的静态函数成员也保证是线程安全的,因为编译器会在编译时将其转换为使用Monitor类。

那么,为什么在这里写下所有这些?好吧,应该有可能建立一个可重用的通用Iterator< T >类(或VB.NET中的Iterator(Of T))。在此类中,您将使用与C#关键字相对应的Yield()Break()方法来实现C#使用的状态机。然后,您可以在函数中使用静态实例(就VB.NET而言),以便它最终可以用几乎相同的代码量完成与C#的yield几乎相同的工作(丢弃类实现本身,因为它将可以无限重复使用)。

我还不太关心Yield自己尝试做的事情,但这应该是可行的。就是说,这也不是一件容易的事,因为C#团队成员Eric Lippert称其为"编译器中最复杂的转换"。

自从一年多以前编写此草稿以来,我也开始相信,直到Visual Studio 2010推出之前,以一种有意义的方式实际上是不可能的,因为它将需要向Iterator类发送多个lambda,因此实际上实际上,我们需要.NET 4对多行lambda的支持。


异步CTP在VB.NET中包括对yield的支持。

有关用法的信息,请参见Visual Basic中的迭代器。

现在,它已包含在Visual Studio 2012的包装盒中!


Visual Studio Magazine中的Bill McCarthy发表了一篇不错的文章,在VB中使用Iterators,它在VB.NET中模拟yield。或者,等待下一个版本的Visual Basic。


幸运的是,现在我们有了yield return
这是我的项目+使用System.Collections.Generic.IEnumerable(T)函数实现接口的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Public Class Status
    Implements IStatus

    Private _statusChangeDate As DateTime
    Public Property statusChangeDate As DateTime Implements IStatus.statusChangeDate
        Get
            Return _statusChangeDate
        End Get
        Set(value As Date)
            _statusChangeDate = value
        End Set
    End Property

    Private _statusId As Integer
    Public Property statusId As Integer Implements IStatus.statusId
        Get
            Return _statusId
        End Get
        Set(value As Integer)
            _statusId = value
        End Set
    End Property

    Private _statusName As String
    Public Property statusName As String Implements IStatus.statusName
        Get
            Return _statusName
        End Get
        Set(value As String)
            _statusName = value
        End Set
    End Property

    Public Iterator Function GetEnumerator() As IEnumerable(Of Object) Implements IStatus.GetEnumerator
        Yield Convert.ToDateTime(statusChangeDate)
        Yield Convert.ToInt32(statusId)
        Yield statusName.ToString()
    End Function

End Class

Public Interface IStatus
    Property statusChangeDate As DateTime
    Property statusId As Integer
    Property statusName As String
    Function GetEnumerator() As System.Collections.Generic.IEnumerable(Of Object)
End Interface

这就是我从外部提取所有属性的方式:

1
2
3
4
5
6
For Each itm As SLA.IStatus In outputlist
    For Each it As Object In itm.GetEnumerator()
        Debug.Write(it &"")
    Next
    Debug.WriteLine("")
Next

我个人只是编写了自己的继承自IEnumerator(Of T)的迭代器类。确实需要花费一些时间,但是我认为最终最好先编写正确的代码,然后再尝试避免使用。我做过的另一种方法是编写一个递归方法,该方法返回IEnumerable(Of T)并仅返回List(Of T)并使用.AddRange


VB.NET具有Iterator关键字https://docs.microsoft.com/zh-cn/dotnet/visual-basic/language-reference/modifiers/iterator

自Visual Studio 2012起


下面的代码给出了输出

2,4,8,16,32

在VB.NET中,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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#中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
    int result=1;
    while (counter < 5)
    {
        result = result * 2;
        counter += 1;
        yield return result;
    }
}

希望这将成为即将发布的VB版本的历史。由于迭代器实际上在新范式(尤其是LINQ与惰性评估结合)中变得越来越重要,据我从Paul Vick的博客了解,这具有很高的优先级。再说一次,Paul不再是VB团队的负责人,而且我还没有时间观看PCD的谈话。

不过,如果您有兴趣,可以在Paul的博客中链接它们。


推荐阅读