VB.NET 从 2.0 迁移到 .Net Standard 2.1 时库中断 - 请求的操作在 TryParse 上不可用

VB.NET library breaks on .Net Standard 2.1 when migrated from 2.0 - Requested operation is not avalaible on TryParse

有一个遗留的 Visual Basic class 库当前针对 .Net Standard 2.0:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" />
  </ItemGroup>
</Project>

里面还有一个class

Public Class Class1
  Public Shared Function TryGetInteger(ByVal value As Object) As Integer
    Dim out As Integer

    If Integer.TryParse(value, out) Then
      Return out
    End If

    Return Integer.MinValue
  End Function

  Public Shared Function BooleanToInt(ByVal bValue As Object) As Integer
    If (bValue = DBNull.Value) Then
      Return 0
    ElseIf bValue = True Then
      Return 1
    Else
      Return 2
    End If
  End Function
End Class

此时一切正常。

但是将 <TargetFramework>netstandard2.0</TargetFramework> 更改为 2.1 会破坏构建并在 Class1.TryGetInteger 函数中出现以下错误:

Requested operation is not available because the runtime library function 'Microsoft.VisualBasic.CompilerServices.Conversions.ChangeType' is not defined

如何解决这个问题?

编辑: 请忽略以下想法:

第一个想法来自这个封闭的 github issue,建议将 <VBRuntime>Default</VBRuntime> 添加到项目文件中,但这会导致错误:

could not find library 'Microsoft.VisualBasic.dll'

但是它已经作为一个包被链接了...有什么想法吗?

编辑: 如评论所述,我对链接的 github 问题的看法有点误导,所以我想我回到了开头Requested operation is not available 错误。

Option Strict 设置为 Off 时,class 方法依赖于 VB 编译器实现的隐式类型转换。

重写代码最安全的方法是使用 de-compiler 并实现编译器使用的转换操作。这可能会涉及很多。

如果在调用 class 方法的代码中有一些合理性,那么可以像下面这样进行简单的重写以避免使用编译器的辅助转换方法。

Public Shared Function TryGetInteger(ByVal value As Object) As Integer
  Dim out As Integer

  If Integer.TryParse(value.ToString, out) Then
    Return out
  End If

  Return Integer.MinValue
End Function

Public Shared Function BooleanToInt(ByVal bValue As Object) As Integer
  If (bValue Is DBNull.Value) Then
    Return 0
  Else
    If TypeOf bValue Is Boolean Then
      Return If(DirectCast(bValue, Boolean), 1, 2)
    Else
      Throw New ArgumentException("type of bValue must be either DbNull or Boolean")
    End If
  End If
End Function

Integer.TryParse() 需要一个 String,但输入参数类型是 Object.

您可以这样解决问题:

Public Shared Function TryGetInteger(value As Object) As Integer
    Dim out As Integer
    If Integer.TryParse(value.ToString(), out) Then Return out

    Return Nothing 'Same as Integer.MinValue in this context
End Function

额外的 ToString() 调用与旧框架隐式为您执行的操作相同。现在你知道了。

但我真的会这样做:

Public Shared Function TryGetInteger(value As String) As Integer
    Dim out As Integer
    If Integer.TryParse(value, out) Then Return out

    Return Nothing
End Function

这可能会引发一整套新的编译器错误,但返回并在这些调用位置修复这些错误是有价值的。大多数错误都可以通过在调用函数时在值的末尾添加 .ToString() 来修复,而任何不能修复的错误几乎肯定是隐藏了错误。

BooleanToInt() 函数类似,我会使用 显式类型 ,并且可能会重载以获得更好的结果,如下所示:

Public Shared Function BooleanToInt(bValue As DBNull) As Integer
    Return 0 'If this overload was selected, we know we want 0
End Function
Public Shared Function BooleanToInt(bValue As Boolean) As Integer
    If bValue Then Return 1
    Return 2
End Function
Public Shared Function BooleanToInt(bValue As Object) As Integer
    Return 2
End Function

根据经验,任何需要直接使用 Object 类型的地方都会吸引错误。这是代码中的异味,需要避免。