8

Próbuję zaimplementować w C++ podobnego szablonu z C# rodzajowych i wzór polityki opartej na this answerWdrażanie wieloparametrowego C++ szablonu jak zachowanie w C# za pomocą wzoru Polityka

To jest przykład wzoru:

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

} 

My przeznaczone zachowanie jest tak:

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 

Ale do tej pory nie był w stanie dokonać .net akceptuje Generic Extension with less arguments than parameters

Mogę nazwać interfejs jawnie, co jest okropnym gadatliwym pokonaniem celu tego wszystkiego.

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

Myślałem, że wokół pracy z obwolutą:

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

Ale wrapper nie może rozwiązać oba typy dla funkcji zawinięte w przypadku braku z:

Błąd 1 typ "MyClass" nie może być użyty jako parametr typu "P" w typie ogólnym lub metodzie "MyClassExtension.wrappedDoSomething (P, U)". Nie ma niejawna konwersja z „MojaKlasa odniesienie do ” „ISomePolicy”

Wszelkie spostrzeżenia, aby rozwiązać ten problem parametrów lub przeprojektować to wszystko są doceniane.


Dla kontekstu będzie to używane do zawijania tłumacza I/O. T w moim przypadku byłby docelowym formatem we/wy, a U reprezentacją obiektu tych danych wykorzystywanych przez mój framework.

Jestem świadomy, że można to łatwo osiągnąć za pomocą delegatów lub interfejsów, ale celem jest, aby użytkownik struktury łatwo tworzył żądane tłumaczenie, a jeśli implementacja nie istnieje, może być trywialnie dodana do wspólnej berło.


EDYCJA: Rozwiązanie ogólnej metody z wnętrza innej ogólnej metody/klasy wydaje się nie działać na mono.

+0

Dlaczego w ogóle potrzebny jest parametr T? –

+0

Aby sprawdzić w czasie kompilacji, '' ISomePolicy 'jest zaimplementowany przez' na MyClass' (patrz 'MyClassExtension') – xvan

Odpowiedz

3

Zwykle zasady nie powinny zawierać danych. Na przykład,

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

Jeśli chcieliby połączyć politykę i dane wówczas można rozważyć inny interfejs

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

lub kombinacji tych dwóch pojęć

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

Zastosowanie:

// 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

Dzięki, miałem pewne problemy z tym. jakie jest znaczenie operatora '=>' w tym kontekście. Nie skompilowano go w ramach 4.5. Zastąpiłem go opakowaniem '{}', ale zasady nie działają zgodnie z oczekiwaniami. Pierwsza nie jest opcją, druga nie blokuje 'var my = new MyClass (0,5); my.doSomething (1) ', Trzeci nie pozwala mi skonstruować' var x = new MySuperClass (0.5); ', chyba że zaimplementowane są wszystkie możliwe polityki. – xvan

+1

@xvan '=>' jest tu używany dla członków funkcji o strukturze wyrażeń i tak, jak oczekiwałeś tylko przydatnej wersji do pisania metod pojedynczego wyrażenia: https://github.com/dotnet/roslyn/wiki/New-Language-Features -w-C% 23-6 # wyrażenie-funkcje-funkcje-członkowie – mbx

3

Próbowałem kodu, ale nawet proste połączenia nie działały z pudełka.Głównym problemem jest to, że MyClass zawiera nieznany element typu "myEement" - tego typu nie można wyprowadzić z parametrów wywołania funkcji. Jednak - jeśli dokonać uogólnienia i pominąć typ obiektu - próbka będzie działać w Out of Box sposób:

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

Co jest do myEement (lub prawdopodobnie oznaczało myelement) - można get to typ na Uruchom- czas w razie potrzeby.

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

Jednak - odbicie zawsze może spowolnić wykonanie. Jeśli nie masz zamiaru tworzyć bardzo ciężkiej hierarchii klas z dużą ilością instancji - to powinno wystarczyć dla twoich potrzeb.

+0

Dzięki, ale implementacja 'doSomething()' zależy od typu 'myElement'. Idea tego wzorca polega na przechwyceniu w czasie kompilacji, gdy próbuje się wykonać nie zaimplementowane wywołanie metody 'doSomething()'. Jeśli idziesz drogą odbicia, istnieje czystsza implementacja za pomocą tabeli refleksji. Przy okazji, dobry połów na literówkę. – xvan

+0

Moje rozwiązanie wykorzystuje niewłaściwe użycie typu - po prostu go wypróbuj. Ale jeśli chcesz mieć mocno typowany typ danych, można go osiągnąć tylko w czasie wykonywania. Możesz stworzyć klasę ISomePolicy w podobny sposób (przekonwertuj interfejs na klasę i przenieś T myElement do tej klasy), która ma mocny typ danych, ale potem sprawiasz, że implementacja jest bardziej złożona. Jest to typowy problem każdego języka - co czyni prostą rzecz, stwarzającą więcej komplikacji na innym końcu. – TarmoPikaro

+0

Jeśli zaakceptuję utratę ostrzeżeń o czasie kompilacji, mogę po prostu przeciążyć 'doSomething (typea a, typeb b)', i throw and exception on 'doSomething (object a, object b)'; W każdym razie spróbuję twojego rozwiązania. – xvan

Powiązane problemy