8

Sto cercando di implementare un C++ come modello con C# generici e modello politica basata sul this answerImplementazione multiparametrica C modello ++ come comportamento in C# utilizzando Criteri modello

Questo è un esempio del modello:

interface ISomePolicy<T,U> 
{ 
    void _doSomething(U u); 
} 


class MyClass<T,U>: 
    ISomePolicy<int, double>, 
    ISomePolicy<int, int> 
    { 

    internal T myElement {get;set;} 

    public MyClass(T Element) { 
     myElement = Element; 
    } 

    void ISomePolicy<int, double>._doSomething(double u) 
    { 
     Console.WriteLine("this is int, double"); 
    } 

    void ISomePolicy<int, int>._doSomething(int u) 
    { 
     Console.WriteLine("this is int, int"); 
    } 

    } 

static class MyClassExtension 
{ 

    //What I want to do 
    public static void doSomething<P, T, U>(this P oTh, U u) where P : MyClass<T, U>, ISomePolicy<T, U> 
    { 
     oTh._doSomething(u); 
    } 

} 

il mio comportamento previsto è come questo:

MyClass<int, double> oClass = new MyClass<int, double>(3); 

    oClass.doSomething(0.5); //This works 
    oClass.doSomething(1); //This works 

    oClass.doSomething("This should fail"); //Breaks at compile time   

    MyClass<string, double> oClass1 = new MyClass<string, double>("sadfsd"); //Not implemented, wasn't able to prevent the construction. 

    oClass1.doSomething(0.4); //Breaks at compile time 

Ma finora non è stato in grado di fare .net accettare Generic Extension with less arguments than parameters

Posso chiamare l'interfaccia esplicitamente, che è orribile verbose che sconfigge lo scopo di tutto questo.

oClass.doSomething < MyClass<int, double>,int,double>(0.5); 

ho pensato di lavorare che in giro con un avvolgitore:

static class MyClassExtension{ 
    private static void wrappedDoSomething<P, T, U>(this P oTh, U u) 
    where P : MyClass<T, U>, ISomePolicy<T, U> 
    { 
     oTh._doSomething(u); 
    } 

    public static void doSomething<T, U>(this MyClass<T, U> oTh, U u) 

    { 
     oTh.wrappedDoSomething<MyClass<T, U>, T, U>(u); 
    } 
} 

Ma l'involucro non può risolvere entrambi i tipi per la funzione avvolto, non riuscendo con:

di errore 1 Il digitare "MyClass" non può essere utilizzato come parametro di tipo "P" nel tipo o metodo generico "MyClassExtension.wrappedDoSomething (P, U)". Non vi sono conversione riferimento implicito da 'MyClass' a 'ISomePolicy'

Eventuali spunti per risolvere il problema parametri o riprogettare tutto questo sono apprezzati.


Per il contesto questo sarebbe utilizzato per avvolgere i traduttori I/O. T nel mio caso sarebbe il formato I/O di destinazione e U la rappresentazione dell'oggetto di quei dati utilizzati dal mio framework.

Sono consapevole che questo può essere facilmente ottenuto con delegati o interfacce, ma l'obiettivo è che l'utente framework istanzia facilmente la traduzione desiderata, e se un'implementazione non esiste, può essere banalmente aggiunta ad un comune interfaccia.


MODIFICA: la risoluzione di un metodo generico dall'interno di un altro metodo/classe generico non sembra funzionare su mono.

+0

Perché è necessario il parametro T? –

+0

Per verificare al momento della compilazione che 'ISomePolicy ' è implementato da 'MyClass' (vedi' MyClassExtension') – xvan

risposta

3

In genere, le politiche non devono contenere dati. Ad esempio,

interface ISomePolicy<T, U> 
{ 
    void _doSomething(T t, U u); 
} 

struct SomePolicyImplementation : 
    ISomePolicy<int, double>, 
    ISomePolicy<int, int>, 
    ISomePolicy<double, double> 
{ 
    void ISomePolicy<int, int>._doSomething(int t, int u) 
     => Console.WriteLine("this is int, int"); 

    void ISomePolicy<int, double>._doSomething(int t, double u) 
     => Console.WriteLine("this is int, double"); 

    void ISomePolicy<double, double>._doSomething(double t, double u) 
     => Console.WriteLine("this is double, double"); 
} 

static class SomePolicyExtension 
{ 
    public static void doSomething<P, T, U>(this P policy, T t, U u) 
     where P : struct, ISomePolicy<T, U> 
     => policy._doSomething(t, u); 
} 

Se volete combinare politiche e dei dati, allora si può prendere in considerazione diversa interfaccia

interface IEmbeddedPolicy<U> 
{ 
    void _doSomething(U u); 
} 

class MyClass<T> : 
    IEmbeddedPolicy<double>, 
    IEmbeddedPolicy<int> 
{ 
    public T Value { get; } 

    public MyClass(T value) { this.Value = value; } 

    void IEmbeddedPolicy<int>._doSomething(int u) 
     => Console.WriteLine("this is T, int"); 

    void IEmbeddedPolicy<double>._doSomething(double u) 
     => Console.WriteLine("this is T, double"); 
} 

static class EmbeddedPolicyExtension 
{ 
    public static void doSomething<E, U>(this E embedded, U u) 
     where E : IEmbeddedPolicy<U> 
     => embedded._doSomething(u); 
} 

o una combinazione di questi due concetti

class MySuperClass<P, T>: 
    IEmbeddedPolicy<double>, 
    IEmbeddedPolicy<int> 
    where P: struct, ISomePolicy<T, double>, ISomePolicy<T, int> 
{ 
    public T Value { get; } 

    public MySuperClass(T value) { this.Value = value; } 

    void IEmbeddedPolicy<int>._doSomething(int u) 
     => new P()._doSomething(this.Value, u); 

    void IEmbeddedPolicy<double>._doSomething(double u) 
     => new P()._doSomething(this.Value, u); 
} 

Usage:

// independent policy 
var policy = new SomePolicyImplementation(); 

policy.doSomething(5, 6); 
policy.doSomething(5, 6.7); 
policy.doSomething(5.3, 6.7); 

// embedded policy 
var my = new MyClass<int>(54); 
my.doSomething(5); 
my.doSomething(89.7); 

// combination 
var x = new MySuperClass<SomePolicyImplementation, int>(53); 
x.doSomething(9); 
x.doSomething(18.3); 
+0

Grazie, ho avuto alcuni problemi con questo. qual è il significato dell'operatore '=>' in questo contesto. Non è stato compilato su 4.5 framework. L'ho sostituito avvolgendo '{}' ma le politiche non funzionano come previsto. Il primo non è un'opzione, il secondo non blocca 'var my = new MyClass (0.5); my.doSomething (1) ', Il terzo non mi permette di costruire' var x = new MySuperClass (0.5); 'a meno che non vengano implementate tutte le politiche possibili. – xvan

+1

@xvan '=>' è usato qui per i membri di funzioni con corpo di espressione e proprio come ci si aspettava solo una versione a portata di mano per scrivere metodi di espressione singola: https://github.com/dotnet/roslyn/wiki/New-Language-Features -in-C% 23-6 # expression-bodied-function-members – mbx

3

Ho provato il tuo codice, ma anche le chiamate semplici non hanno funzionato fuori dagli schemi.Il problema principale è che MyClass contiene un tipo di elemento sconosciuto "myEement": quel tipo non può essere dedotto dai parametri di chiamata della funzione. Tuttavia - se si effettua una generalizzazione e omette tipo di oggetto - il campione funzionerà in fuori scatola modo:

using System; 
using System.Collections.Generic; 

interface ISomePolicy<U> 
{ 
    void _doSomething(U u); 
} 

public class MyClass<U> : 
    ISomePolicy<double>, 
    ISomePolicy<int> 
{ 
    internal object myEement { get; set; } 

    public MyClass(object Element) 
    { 
     myEement = Element; 
    } 

    void ISomePolicy<double>._doSomething(double u) 
    { 
     Console.WriteLine("this is double"); 
    } 

    void ISomePolicy<int>._doSomething(int u) 
    { 
     Console.WriteLine("this is int"); 
    } 
} 

static class MyClassExtension 
{ 
    public static void doSomething<P, U>(this P oTh, U u) where P : ISomePolicy<U> 
    { 
     oTh._doSomething(u); 
    } 
} 

class Program 
{ 
    static void Main() 
    { 
     MyClass<double> oClass = new MyClass<double>(3); 
     oClass.doSomething(0.5); //This works 
     oClass.doSomething(1); //This works    
     //oClass.doSomething("Will not work"); 
    } 
} 

Cosa sta a myEement (o probabilmente significava MyElement) - si può ottenere di suo tipo a run- tempo se necessario.

myElement.GetType(), or cast to it - e.g. 
if(myElement is int) DoSomethingWithInt((int) myElement); 

Tuttavia, la riflessione potrebbe sempre rallentare l'esecuzione. Se non hai intenzione di creare una gerarchia di classi super-pesanti con una quantità enorme di istanze, allora questo dovrebbe essere sufficiente per le tue esigenze.

+0

Grazie, ma l'implementazione 'doSomething()' dipende dal tipo 'myElement'. L'idea di questo modello è di catturare in fase di compilazione quando viene tentata una chiamata non implementata a 'doSomething()'. Se si sta seguendo la riflessione, c'è un'implementazione più pulita utilizzando una tabella di riflessione. A proposito, buona cattura sull'errore di battitura. – xvan

+0

La mia soluzione rileva un utilizzo improprio del tipo: provalo. Ma se si desidera avere un tipo di dati fortemente tipizzato, è possibile ottenerlo solo in fase di esecuzione. È possibile creare classe come ISomePolicy (converte l'interfaccia in classe e spostare T myElement in quella classe) che ha un tipo di dati forte, ma in tal caso si rende l'implementazione più complessa. È un problema tipico di qualsiasi lingua: rendere una cosa semplice crea più complessità da un'altra parte. – TarmoPikaro

+0

Se accetto di perdere gli avvisi di compilazione, potrei semplicemente sovraccaricare 'doSomething (typea a, typeb b)', e lanciare ed eccezione su 'doSomething (oggetto a, oggetto b)'; Comunque proverò la tua soluzione. – xvan