无法从C#中解析同时被重写和重载的F#方法

22
以下是F#代码,声明了一个基类和一个派生类。基类有一个虚方法'Test'并有一个默认实现,而派生类则重写了基类的方法,并添加了一个新的重载'Test'方法。这段代码编译时没有问题,而且访问派生类中的任意一个'Test'方法也不会有任何问题。
module OverrideTest
  [<AbstractClass>]
  type Base() =
    abstract member Test : int -> int
    default this.Test x = x + 1

  type Descendant() =
    inherit Base()
    override this.Test x    = x - 1
    member this.Test (x, y) = x - y

尝试从C#调用子类对'Test'的重写会导致编译错误:

var result = td.Test(3); <- 没有接受1个参数的方法“Test”的重载

完整的C#代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Client
{
  class Program
  {
    static void Main(string[] args)
    {
      var td = new OverrideTest.Descendant();
      var result = td.Test(3);
      Console.WriteLine(result);
      Console.ReadKey();
    }
  }
}

奇怪的是,VisualStudio 的智能感知看到了这两个重载函数,并为两者提供了正确的签名。在构建失败之前,它没有给出任何警告或错误提示,只有在之后才会突出显示该行。

我已经完全在C#中重新实现了这种情况,并且没有遇到同样的问题。

有人知道这里发生了什么吗?


2
我在反编译器中唯一奇怪的事情是基本方法显示为“override”,而不是“virtual”。似乎这是因为基本方法没有“newslot”属性。 - svick
1
我认为这是C#编译器或F#编译器中的一个bug,但我不确定是哪一个(我猜测是C#)。 - svick
2
@svick - Base 方法缺少 newslot 只意味着如果 它的 基类(在本例中为 obj)更改为具有正确签名的虚拟 Test 方法,则 Base.Test 将覆盖它而不是隐藏它。我认为 F# 最好包含 newslot 以避免版本问题,但我看不出这会在这种情况下引起任何问题。 - kvb
1
这很可能是一个 bug —— 我们会调查一下。感谢您的报告!Donna Malayeri (MSFT) - lindydonna
现在是2013年9月,使用VS2012和.NET 4.5仍然存在这个问题。现在IL的唯一区别是C#中的静态成员具有“hidebysig”,而F#中没有:F# .method public static bool Equals(string a, string b) cil managedC# .method public hidebysig static bool Equals(string a, string b) cil managed - Philip P.
显示剩余2条评论
1个回答

17
毫无疑问,您应该知道,如果省略Descendant类型中的Test(x,y)成员,或者仅将其重命名为Test2(x,y),则C#代码将按预期编译和运行。查看生成的原始Descendant类型的IL可提供线索:
.method public hidebysig virtual
    instance int32 Test (
        int32 x
    ) cil managed ...

.method public 
    instance int32 Test (
        int32 x,
        int32 y
    ) cil managed ...

请注意,Test(x,y)方法上没有hidebysig属性。 ECMA CLI规范hidebysig有以下说明(第15.4.2.2节,粗体强调是我的)。

hidebysig是为工具提供的,VES会忽略它。它指定声明的方法隐藏所有基类类型的方法,这些方法具有匹配的方法签名;当省略时,该方法应该隐藏所有同名方法,而不管签名如何。

因此,F#编译器省略了hidebysig属性,这意味着Test(x,y)方法隐藏了所有其他名为Test的方法。尽管hidebysig仅“供工具使用”,但似乎C#编译器是使用之一!
在我看来,这可能是F#编译器中的一个错误,但由于我从未查看过F#规范,因此这种行为可能是允许/指定的。

+1:不得不读几遍才能理解“同名”的含义。在我看来,句子的最后一部分也应该是加粗的。 - leppie
这一定是个bug,对吧? - Daniel
@Daniel:我想不出为什么应该省略 hidebysig。(我之前也不知道这个) - leppie

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