2009-09-23 3 views
16

En C#, je peux mettre en œuvre une interface générique deux fois sur une classe, en utilisant deux paramètres de type-différents:La mise en œuvre de la même interface à différents instanciations génériques

interface IFoo<T> { void Foo(T x); } 

class Bar : IFoo<int>, IFoo<float> 
{ 
    public void Foo(int x) { } 
    public void Foo(float y) { } 
} 

Je voudrais faire la même chose en F #:

type IFoo<'a> = abstract member Foo : 'a -> unit 

type Bar() = 
    interface IFoo<int> with 
     [<OverloadID("int")>] 
     member this.Foo x =() 

    interface IFoo<float> with 
     [<OverloadID("float")>] 
     member this.Foo x =() 

Mais il donne une erreur du compilateur:

This type implements or inherits the same interface at different generic instantiations 'IFoo<float>' and 'IFoo<int>' . This is not permitted in this version of F#.

Je ne peux pas trouver discussion of this issue sur le web. Une telle utilisation est-elle mal vue pour une raison quelconque? Est-il prévu d'autoriser cela dans une prochaine version de F #?

+0

Feature prévu pour F # 4.0 http://fslang.uservoice.com/forums/245727- f-language/suggestions/5663504-allow-to-implement-the-same-interface-at-different – foobarcode

+0

Demande de tirage peut être trouvé à: https://github.com/Microsoft/visualfsharp/pull/18 – forki23

Répondre

11

En ce moment je ne sais pas de plans pour permettre cela. . La caractéristique has been planned est, au moins partiellement (voir les commentaires) implémentée en F # 4.0. Je pense que les seules raisons pour lesquelles il est actuellement interdit sont qu'il est non trivial à implémenter (en particulier avec l'inférence de type F #), et cela se produit rarement dans la pratique (je me souviens seulement d'un client). Étant donné une quantité infinie de temps et de ressources, je pense que cela serait permis (je peux imaginer que cela sera ajouté à une future version du langage), mais pour l'instant, il ne semble pas que ce soit une fonctionnalité qui en vaut la peine de soutenir. (. Si vous connaissez un dossier solide motivation, s'il vous plaît courriel [email protected])

EDIT

A titre d'expérience pour les curieux, j'ai écrit cette C#:

public interface IG<T> 
{ 
    void F(T x); 
} 
public class CIG : IG<int>, IG<string> 
{ 
    public void F(int x) { Console.WriteLine("int"); } 
    public void F(string x) { Console.WriteLine("str"); } 
} 

et référencé à partir F # (avec des commentaires suggérant les résultats)

let cig = new CIG() 
let idunno = cig :> IG<_> // type IG<int>, guess just picks 'first' interface? 
let ii = cig :> IG<int> // works 
ii.F(42)     // prints "int" 
let is = cig :> IG<string> // works 
is.F("foo")    // prints "str" 

donc c'est ce qui se passe généralement sur ce genre de choses « limite » avec F # - F # peut consommer ce genre de choses ok, même si vous ne pouvez pas l'auteur de la SA moi des trucs de l'intérieur de la langue.

+2

Er, donc Comment l'inférence de type F # traiterait-elle de tels types écrits en C#, alors? En ce qui concerne la justification, eh bien ... les spécifications de l'ECMA CLI définissent différentes catégories de conformité CLS; l'un d'entre eux est "CLS extender". Une exigence pour cela est: "Capable de ... Implémenter une interface compatible CLS." –

+0

Pour développer le commentaire de Pavel, que se passe-t-il si vous définissez une interface non générique 'I' en C# qui étend à la fois' IG 'et' IG '? Cette interface peut-elle être implémentée à partir de F #? – kvb

+0

@kvb, non, cette interface ne peut pas être implémentée à partir de F #. – Brian

0

Il existe un moyen raisonnable mais pas élégant de créer un nouveau type pour chaque interface. Voici un exemple de consommation d'événements multiples à partir d'un ESB (nSvcBus) qui exige que chaque événement corresponde à une interface implémentée. Le premier type ci-dessous contient le code « gestionnaire » générique, les autres types mettre en œuvre juste l'interface et appeler au gestionnaire générique

type public nSvcBusEvents() = 

    member this.HandleEvents(msg:IEvent) =() 
     //handle messages ie: let json = JsonConvert.SerializeObject(msg) 

type public ActionLoggedHandler() = 
    interface IHandleMessages<Events.ActionLoggedEvent> with 
     member this.Handle(msg : ActionLoggedEvent) = 
      nSvcBusEvents().HandleEvents(msg) 

type public ActionCompletedHandler() = 
    interface IHandleMessages<Events.ActionCompletedHandler> with 
     member this.Handle(msg : ActionCompletedHandler) = 
      nSvcBusEvents().HandleEvents(msg) 

type public ActionFailedHandler() =  
    interface IHandleMessages<Events.ActionFailedHandler> with 
     member this.Handle(msg : ActionFailedHandler) = 
      nSvcBusEvents().HandleEvents(msg) 
Questions connexes