2011-11-23 14 views
21

Następujący kod F # deklaruje klasy podstawowe i potomne. Klasa podstawowa ma wirtualną metodę "Test" z domyślną implementacją. Klasa potomna przesłania metodę klasy bazowej, a także dodaje nową przeładowaną metodę "Test". Ten kod kompiluje dobrze i nie przedstawia żadnych problemów podczas uzyskiwania dostępu do jednej z metod potomnych "Test".Nie można rozwiązać metody F #, która została zarówno nadpisana, jak i przeciążona z C#

F # Kod:

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 

Jednak próby wywołania przesłonić potomek dotyczącą 'test' z C# powoduje błąd kompilacji:

var result = td.Test(3); <- No overload for method 'Test' takes 1 arguments

Pełne C# Kod:

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(); 
    } 
    } 
} 

Dziwne jest to, że intellisense VisualStudio widzi dwie przeciążone funkcje i zapewnia s poprawne podpisy dla obu. Nie daje ostrzeżeń ani błędów, zanim kompilacja się nie powiedzie, a następnie podświetla tylko linię.

Ponownie wdrożyłem ten scenariusz w pełni w języku C# i nie napotkano tego samego problemu.

Ktoś ma jakieś pomysły, co tu się dzieje?

+0

Czy porównałeś emisję IL w obu językach? – Oded

+0

Co się stanie, jeśli kod C# zostanie uaktualniony do klasy bazowej przed wywołaniem metody? – Brian

+0

@Brian: To działa. – Daniel

Odpowiedz

17

bez wątpienia jesteś świadom, że jeśli pominąć człon od rodzaju DescendantTest(x,y) - lub po prostu zmienić jego nazwę Test2(x,y) - to kod C# będzie skompilować i uruchomić zgodnie z oczekiwaniami.

Patrząc na IL generowanego dla oryginalnego Descendant typu oferuje pojęcia:

.method public hidebysig virtual 
    instance int32 Test (
     int32 x 
    ) cil managed ... 

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

Zauważ, że nie ma hidebysig atrybut na metodzie Test(x,y).

Na temat ECMA CLI specification można uzyskać następujące informacje: hidebysig. (Sekcja 15.4.2.2, nacisk pogrubione jest mój.)

hidebysig is supplied for the use of tools and is ignored by the VES. It specifies that the declared method hides all methods of the base class types that have a matching method signature; when omitted, the method should hide all methods of the same name, regardless of the signature.

Tak, F # kompilator pomija atrybut hidebysig, co oznacza, że ​​metoda Test(x,y) ukrywa wszystkie inne metody o nazwie Test. Chociaż hidebysig jest tylko "do użytku narzędzi", wydaje się, że kompilator C# jest jednym z tych narzędzi, które go używa!

Wygląda na to, że może to być błąd w kompilatorze F #, ale ponieważ nigdy nie patrzyłem na specyfikację F #, zawsze jest możliwe, że jest to dozwolone/określone zachowanie.

+0

+1: Musiałem przeczytać go kilka razy, aby uchwycić "o tej samej nazwie". IMO ostatnia część zdania powinna być ** BOLD ** również. – leppie

+0

To jest błąd. Dobrze? – Daniel

+0

@Daniel: Nie mogę wymyślić powodu, dla którego 'hidebysig' powinien zostać pominięty. (Nigdy też o tym nie wiedziałem) – leppie