2016-06-04 9 views
7

Desidero creare una libreria che crei oggetti utilizzando una funzione definita dall'utente e li modifichi utilizzando un'altra funzione definita dall'utente.OO alternativa al polimorfismo in F # quando si chiama F # da C#

Ho uno sfondo OCaml e vedo un modo abbastanza semplice per la sua attuazione:

//user side 
type userType = { mutable time : float } 

let userInitialization() = { time = 0. }  
let userModification t = t.time <- t.time+1. 

//my side 
let algo initialization modification n = 
    let a = Array.init n (fun _ -> initialization()) 
    modification a.[0] 
    a 

Il problema è che voglio quella libreria per essere facile da chiamare da C#: con funzioni come argomenti è probabilmente una cattiva idea.

interfaccia e la classe astratta (che sarebbe ereditata da userType) sembra essere la soluzione al solito OO, ma non posso inizializzare un oggetto se non so l'UserType (non ancora definito) rendendo la fase di inizializzazione, dentro la mia funzione, impossibile.

L'unica soluzione che posso pensare è di chiedere all'utente un'istanza del suo userType come argomento che verrebbe utilizzato per chiamare l'inizializzazione, ma che sembra molto poco elegante.

C'è un modo per risolvere questo problema?

+2

rendere l'interfaccia generica? –

+1

Se ci si aspetta un uso intenso di C#, probabilmente si sta meglio con un'interfaccia, ma c'è anche 'Microsoft.FSharp.Core.FuncConvert' per la conversione tra C#' Funcs' o 'Actions' e F #' FSharpFuncs'. Potresti usarlo sul lato C# insieme al tuo codice corrente. – scrwtp

risposta

5

Si potrebbe definire un'interfaccia simile a questo:

type IInitAndModify<'T> = 
    abstract member CreateNew : unit -> 'T 
    abstract member Modify : item : 'T -> unit 

e, forse, anche un oggetto che l'utente può utilizzare per passare al vostro implementazione:

type Algo<'T>() = 
    member this.Execute (initAndModify : IInitAndModify<'T>, n) = 
     algo initAndModify.CreateNew initAndModify.Modify n 

da C#, sembra che questo:

public interface IInitAndModify<T> 
{ 
    T CreateNew(); 
    void Modify(T item); 
} 

public class Algo<T> 
{ 
    public Algo(); 

    public T[] Execute(IInitAndModify<T> initAndModify, int n); 
} 

Uno sviluppatore client può usare in questo modo:

public class UserType 
{ 
    public float Time { get; set; } 
} 

public class UserInitAndModify : IInitAndModify<UserType> 
{ 
    public UserType CreateNew() 
    { 
     return new UserType { Time = 0 }; 
    } 

    public void Modify(UserType item) 
    { 
     item.Time++; 
    } 
} 

e scrivere un programma come questo:

static void Main(string[] args) 
{ 
    var a = new Algo<UserType>(); 
    var values = a.Execute(new UserInitAndModify(), 10); 
    foreach (var v in values) 
     Console.WriteLine(v.Time); 
} 

Quando si esegue il metodo di cui sopra Main, l'output è questo:

1 
0 
0 
0 
0 
0 
0 
0 
0 
0 
Press any key to continue . . . 
+0

Questo è esattamente quello che stavo cercando, grazie! –

5

sarei propenso a respingere l'idea che si shouldn 'esporre funzioni come argomenti in una libreria esposta a C#, sta diventando piuttosto comune farlo, è necessario solo guardare LINQ, TPL, ecc. Non credo che troppi sviluppatori C# sarebbero spaventati da quello.

Vorrei tuttavia suggerire di evitare di esporre le funzioni con argomenti in curry a C# in quanto non sono affatto convenienti con cui lavorare.

Si può facilmente avvolgere l'algoritmo in una funzione che accetta argomenti in forma tupla ed espone System.Func e System.Action s a C#.

let csAlgo (initialisationFunc : System.Func<'a>, 
      modificationFunc : System.Action<'a>, 
      n : int) = 
    algo (initialisationFunc.Invoke) (modificationFunc.Invoke) n 

in C# si potrebbe quindi fare questo:

var res = Module.csAlgo(() => new UserType(0), t => t.Time = t.Time + 1, 16); 

Come una piccola parte, è anche possibile utilizzare l'attributo CompiledName per si può mettere le convenzioni involucro giusto in ogni lingua.Tag vostra funzione

[<CompiledName("ExampleFunction")>] 
let exampleFunction() = 1 

Poi in C#, sembra che questo:

var num = Module.ExampleFunction(); 
+1

Grazie per la tua risposta, ho la sensazione che uno sviluppatore C# preferirebbe alimentare un'interfaccia orientata agli oggetti, ma tu segnami un buon punto che terrò in memoria. –