使用Nothing检查的变量声明

5

非常(非常)经常,我们需要编写以下类似的内容:

Dim Data = GetSomeData()
If Data IsNot Nothing Then
  Data.DoSomething()
Else
  ...
End If

也许我的期望徒劳无功,但我真诚地希望VB.Net有类似以下结构的语法:
IfExists Data = GetSomeData() Then
  Data.DoSomething()
Else
  ...
End IfExists

在我的梦想中,它可以做两件重要的事情:
  1. 不需要额外的“Nothing”检查
  2. 变量A在块外部不可见,因此不会被错误地后续使用(就像“Using”或“With”一样)
有没有类似的东西我还没有找到?
谢谢!
编辑: 受Bjørn-Roger Kringsjå答案的启发,我想出了一个能满足我需求的东西(因为VB.Net的缺陷而感到谦卑):
<Extension()>
Public Sub IfExists(Of T)(This As T, DoIfNotNothing As Action(Of T), Optional DoIfNothing As Action = Nothing)
    If This IsNot Nothing Then
        DoIfNotNothing(This)
    ElseIf DoIfNothing IsNot Nothing Then
        DoIfNothing()
    End If
End Sub

然后我可以像这样调用它(false 部分是可选的)。
GetSomeData().IfExists(Sub(Data) Data.DoSomething())

或者

GetSomeData().IfExists(Sub(Data) Data.DoSomething(), Sub() DoSomethingElse())

如果你只需要一个函数,那么把if语句放在一个函数中并调用YourNewfunction(GetSomeData())。 - the_lotus
只是出于好奇:如果 *"不在块外可见"*,那么你打算在什么时候/地方声明变量 A 呢?也许像这样吧:IfExists Dim A As Object = GetSomeData() Then - Bjørn-Roger Kringsjå
@Bjørn-Roger Kringsjå:就像Using T = New DataTableFor i = 0 to 9一样。现在已经可以实现,也不需要Dim。类型推断会自动确定数据类型。 - Majnu
@majnu 这是一个修辞问题。它不被支持,因此不能完成。你列出的所有示例都得到支持,因此可以完成。 - Bjørn-Roger Kringsjå
1
C# 中,你可以这样写 if((A=GetSomeData())!=null) { ... }。啊,C# 的强大之处(借鉴自 C)。 - John Alexiou
显示剩余3条评论
3个回答

4

正如其他人所说,也暗示了我,这是不可能的。只是想分享一个第三种解决方案。这一次我们将使用代理

  1. 无需额外的行来检查空值
  2. 变量A在块外部不可见,因此不能在以后错误地使用它。

实现

Public Module Extensions

    Public Sub IfExists(Of T)(testExpr As T, trueDlg As Action(Of T), falseDlg As Action)
        If (Not testExpr Is Nothing) Then
            trueDlg.DynamicInvoke(New Object(0) {testExpr})
        Else
            falseDlg.DynamicInvoke(New Object(-1) {})
        End If
    End Sub

End Module

使用方法

IfExists(GetSomeData(),
         Sub(A As Object)
             'We have something (A)
         End Sub,
         Sub()
             'We have nothing
         End Sub
    )

更短:

IfExists(GetSomeData(), Sub(A As Object)
                                'We have something (A)
                            End Sub, Sub()
                                         'We have nothing
                                     End Sub)

或者,最短的版本:
IfExists(GetSomeData(), Sub(A As Object) Debug.WriteLine(A.ToString()), Sub() Debug.WriteLine("Nothing"))

所以有一个向导 :) 让我看看能否再微调一下。感谢您的启发! - Majnu
@Bjørn-Roger Kringsjå:抱歉,看起来我无法在评论中发布多行代码。但是如果我创建一个全新的条目,我将需要回答自己的问题。这不是我想要的,因为如果没有更厉害的巫师出现,那么荣誉将归于您;)还有其他方法吗? - Majnu
@Bjørn-Roger Kringsjå:好的,我编辑了我的帖子并分享了我的想法。我也在想为什么你使用DynamicInvoke。您认为怎么样,巫师? - Majnu
@majnu 我本可以使用 Invoke 而非 DynamicInvoke,但从你的代码中我想象你是一个 "Option Strict Off" 的人。(无冒犯之意) - Bjørn-Roger Kringsjå
@Bjørn-Roger Kringsjå: 是的,我确实喜欢类型检查,但有时它对我来说可能太严格了。但是不论这个,你觉得我根据你的答案想出来的东西怎么样?我自己真的很喜欢它!但这也许是因为我很喜欢函数式语言,那里的代码经常是这样的。 - Majnu
显示剩余5条评论

1
很抱歉,目前在VB.NET中没有类似的功能。您可以编写以下函数来近似模拟该行为:
Public Function Assign(ByRef target As Object, value As Object) As Boolean
    target = value
    Return (target IsNot Nothing)
End Function

然后你可以像这样使用它:
Dim A As SomeType
If Assign(A, GetSomeData()) Then
    ' ...
Else
    ' ...
End If

但是,正如你所指出的那样,这并没有真正解决你提出的问题。它仍然是额外的一行代码,并且变量仍然不能被限定在只能在正确分配的块中访问。

0

第一个点是不可能的。没有办法在单个语句中声明变量并检查它。

第二个点有点可能。VB.NET不支持创建自己的块作用域,但您可以通过滥用其他块来实现它。将您的代码插入单次使用的Loop While块中:

Do
    Dim A = GetSomeData()
    If A IsNot Nothing Then
        ...
    Else
        ...
    End If
Loop While False

Do块将被输入,其中A在该块中声明为本地变量,然后该块将立即在While False处退出,并且A将不再可访问。

更好的简化方法可能是通过将代码重构为更多、更小的方法,使得If ... Else成为整个方法,没有可能意外访问以后过时的变量。当您这样做时,在简单情况下还可以从方法中提前退出,而不是保持IfElse分支:

    Dim A = GetSomeData()
    If A Is Nothing Then
        ...
        Exit Sub
    End If
    ...
End Sub

在作用域方面,我经常按照您在顶部建议的方式进行。不过,我使用 With Nothing 块。这样做不会让下一个代码阅读器认为还有其他事情正在发生。 - Majnu
也许你说得对,这可能做不到。但我会把问题保持开放一段时间。也许会有神奇的大佬路过提供绝妙的解决方案。只是做梦罢了 :) - Majnu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接