2011-11-23 6 views
21

Il seguente codice F # dichiara classi di base e discendenti. La classe base ha un metodo virtuale 'Test' con un'implementazione predefinita. La classe discendente sovrascrive il metodo della classe base e aggiunge anche un nuovo metodo "Test" sovraccarico. Questo codice viene compilato correttamente e non presenta problemi durante l'accesso a uno dei metodi "Test" del discendente.Impossibile risolvere un metodo F # sovrascritto e sovraccaricato da C#

F # Codice:

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 

Tuttavia, il tentativo di richiamare esclusione del discendente di 'Test' da C# i risultati in un errore di compilazione:

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

La piena C# Codice:

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

La cosa strana è che l'intellisense di VisualStudio vede le due funzioni sovraccariche e fornisce s firme corrette per entrambi. Non dà avvertimenti o errori prima che la compilazione fallisca e mette in evidenza solo la linea in seguito.

Ho reimplementato questo scenario completamente in C# e non ho incontrato lo stesso problema.

Qualcuno ha qualche idea di cosa sta succedendo qui?

+0

Hai confrontato l'IL emesso in entrambe le lingue? – Oded

+0

Cosa succede se nel codice C# viene trasmesso alla classe base prima di chiamare il metodo? – Brian

+0

@Brian: Funziona. – Daniel

risposta

17

senza dubbio siete consapevoli del fatto che se si omette il membro Test(x,y) dal tipo Descendant - o semplicemente rinominarlo Test2(x,y) - allora il codice C# verrà compilato ed eseguito come previsto.

Guardando il IL generato per il vostro tipo di originale Descendant offre un indizio:

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

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

Si noti che non c'è hidebysig attributo sul metodo Test(x,y).

Il ECMA CLI specification ha il seguente da dire su hidebysig. (Sezione 15.4.2.2, l'accento in grassetto è mio.)

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.

Così, il compilatore F # omette l'attributo hidebysig, il che significa che il metodo Test(x,y) nasconde tutti gli altri metodi di nome Test. Sebbene hidebysig sia solo "per l'uso di strumenti", sembra che il compilatore C# sia uno di quegli strumenti che lo utilizza!

Questo mi sembra un bug nel compilatore F #, ma poiché non ho mai visto le specifiche F # è sempre possibile che questo sia un comportamento consentito/specificato.

+0

+1: ho dovuto leggerlo alcune volte per cogliere "lo stesso nome". IMO l'ultima parte della frase dovrebbe essere ** BOLD ** anche. – leppie

+0

Questo è _got_ come bug. Destra? – Daniel

+0

@Daniel: Non riesco a pensare a una ragione per cui "hidebysig" dovrebbe essere omesso. (Non ho mai saputo nemmeno di questo) – leppie