【问题标题】:VB Compiler does not do implicit casts to Object?VB 编译器不对对象进行隐式强制转换?
【发布时间】:2011-04-10 13:50:48
【问题描述】:

我最近报告了一个关于我的 API 的奇怪问题。基本上出于某种原因,当与 VB 代码一起使用时,VB 编译器在尝试调用 ToString() 方法时不会对 Object 进行隐式强制转换。

以下是一个最小的代码示例,首先是 C#,其次是 VB:

    Graph g = new Graph();
    g.LoadFromEmbeddedResource("VDS.RDF.Configuration.configuration.ttl");

    foreach (Triple t in g.Triples)
    {
        Console.WriteLine(t.Subject.ToString());
    }

上面的编译并运行良好,而下面的则不行:

    Dim g As Graph = New Graph()
    g.LoadFromEmbeddedResource("VDS.RDF.Configuration.configuration.ttl")

    For Each t As Triple In g.Triples
        Console.WriteLine(t.Subject.ToString())
    Next

第二个 VB 示例给出以下编译器异常:

重载解析失败,因为没有 可访问的“ToString”接受这个 参数数量。

这似乎是由于我试图写入控制台的属性t.Subject 的类型已明确定义了带参数的ToString() 方法。 VB 编译器似乎期望使用其中之一,并且似乎没有隐式转换为 Object 并使用标准的 Object.ToString() 方法,而 C# 编译器则使用。

有什么办法可以解决这个问题吗? VB 编译器选项,还是最好只是确保属性的类型(在本例中为接口)显式定义未参数化的 ToString() 方法以确保与 VB 的兼容性?

编辑

这是 Lucian 要求的其他详细信息

  1. 图形是一个接口的实现,但这实际上是无关紧要的,因为它是INode 接口,这是t.Subject 返回的类型,这是问题所在。
    INodeToString() 定义了两个重载两者都带参数
  2. 是的,这是编译时错误
  3. 不,我不使用按名称隐藏,API 都是用 C# 编写的,所以如果我想的话,我无法生成那种 API

请注意,我已经在界面中添加了显式未参数化的 ToString() 重载,从而解决了 VB 用户的问题。

【问题讨论】:

  • 不需要演员表。 .ToString() 被定义为对象类的公共方法,该对象类是所有 .net 类的子类。所以它是您创建的每个类的成员。即使您已经覆盖了该方法,您也无法将访问修饰符更改为internal - “无法访问'ToString'”让我想到了这个想法。
  • 是的,我知道ToString() 的定义方式和位置,这就是为什么我对VB 编译器以这种方式抱怨感到如此困惑的原因。我虽然报告这个问题的人刚刚做了一些愚蠢的事情,但我自己复制了它,所以这是 VB 编译器的某种问题。
  • @Zebi 而且在这种情况下,所有ToString() 重载都是由接口定义的,因此它们必须是公共的,而且相关接口的所有具体实现都会覆盖默认的ToString() 方法除了实现额外的重载之外,还可以适当地实现

标签: c# .net vb.net compiler-construction casting


【解决方案1】:

RobV,我是 VB 规范负责人,所以我应该能够回答您的问题,但我需要澄清一下...

  • “图表”上定义的重载是什么?如果您可以制作一个独立的复制品,那将会有所帮助。在不知道重载候选者的情况下很难解释重载行为:)

  • 您说它因“编译器异常”而失败。那真的不存在。你的意思是“编译时错误”吗?还是“运行时异常”?

  • 需要检查的是您是否依赖于任何类型的“hide-by-name”与“hide-by-sig”行为。 C# 编译器只发出“hide-by-sig”API; VB 编译器可以根据您是否使用“Shadows”关键字发出任何一种。

  • C#重载算法就是一层一层的往上走继承层次结构,直到找到一个可能的匹配; VB重载算法就是同时查看继承层次的各个层次,看哪一个最匹配。这全是理论性的,但是通过对您的问题的一个小的独立重现,我可以解释它在实践中的含义。

汉斯,我不认为你的解释是正确的。您的代码给出了编译时错误“BC30455:未为 ToString 的参数 'mumble' 指定参数”。但是 RobV 经历过“重载解析失败,因为没有可访问的 'ToString' 接受这个数量的参数”。

【讨论】:

  • 为我的问题添加了更多详细信息
【解决方案2】:

这是此行为的重现。它还向您展示了解决方法,使用 CObj():

Module Module1
    Sub Main()
        Dim itf As IFoo = New CFoo()
        Console.WriteLine(itf.ToString())        '' Error BC30455
        Console.WriteLine(CObj(itf).ToString())  '' Okay
    End Sub
End Module

Interface IFoo
    Function ToString(ByVal mumble As Integer) As String
End Interface

Class CFoo
    Implements IFoo
    Function ToString1(ByVal mumble As Integer) As String Implements IFoo.ToString
        Return "foo"
    End Function
End Class

我认为这在 VB.NET 语言规范第 11.8.1 章“重载方法解析”中有注释:

这条规则的理由是 如果一个程序是松散类型的 (也就是说,大多数或所有变量都是 声明为对象),重载 解决可能很困难,因为 来自 Object 的所有转换都是 缩小。而不是拥有 重载决议在许多失败 情况(需要强类型 方法调用的参数), 解决适当的重载 调用的方法被推迟到运行 时间。这允许松散类型 无需额外调用即可成功 演员表。

一个不幸的副作用, 然而,是执行 后期绑定调用需要强制转换 将目标调用为对象。如果是 一个结构值,这意味着 值必须装箱到临时。如果 最终调用的方法试图 改变结构的一个字段,这个 更改将丢失一次方法 返回。

接口被排除在外 特殊规则,因为后期绑定 总是解决反对的成员 运行时类或结构类型, 可能有不同的名字 他们接口的成员 实施。

不确定。我将其音译为:VB.NET 是一种松散类型的语言,其中许多对象引用通常是后期绑定的。这使得方法重载决议很危险。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-04
    • 1970-01-01
    • 2015-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多