Внедрение многопараметрического шаблона С++, как поведение на С# с использованием шаблона политики
Я пытаюсь реализовать шаблон типа С++ с общим набором С# и шаблоном политики на основе этого ответа
Это образец шаблона:
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);
}
}
Мое предполагаемое поведение выглядит следующим образом:
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
Но пока я не смог сделать .net accept Generic Extension с меньшими аргументами, чем параметры
Я могу вызвать интерфейс явно, что является ужасным подробным, преследующим цель всего этого.
oClass.doSomething < MyClass<int, double>,int,double>(0.5);
Я думал о том, чтобы работать с оберткой:
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);
}
}
Но оболочка не может разрешить оба типа для завернутой функции, не выполнив следующие действия:
Ошибка 1 Тип "MyClass" не может использоваться как параметр типа "P", в общем типе или методе 'MyClassExtension.wrappedDoSomething(P, U)'. Здесь нет неявное преобразование ссылок из "MyClass" в 'ISomePolicy'
Любые идеи для исправления проблемы параметров или реорганизации всего этого оцениваются.
Для контекста это будет использоваться для переноса переводчиков ввода-вывода. T
в моем случае будет целевым форматом ввода/вывода, а U
- представлением объекта для данных, используемых моей каркасом.
Я знаю, что это может быть легко достигнуто с помощью делегатов или интерфейсов, но цель заключается в том, что пользователь фреймворка легко создает требуемый перевод, а если реализация не существует, его можно добавить тривиально в общий интерфейс.
EDIT: разрешение универсального метода из другого общего метода/класса не работает на моно.
Ответы
Ответ 1
Обычно политики не должны содержать данные. Например,
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);
}
Если вы хотите объединить политики и данные, вы можете рассмотреть другой интерфейс
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);
}
Или сочетание этих двух понятий
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);
}
Использование:
// 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);
Ответ 2
Пробовал ваш код, но даже простые вызовы не работали из коробки. Основная проблема заключается в том, что MyClass содержит неизвестный тип элемента 'myEement' - этот тип не может быть выведен из параметров вызова функции. Однако, если вы делаете обобщение и опускаете тип объекта, ваш образец будет работать вне поля:
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");
}
}
Что такое myEement (или вы, вероятно, подразумевали myElement) - вы можете получить его тип во время выполнения, если это необходимо.
myElement.GetType(), or cast to it - e.g.
if( myElement is int ) DoSomethingWithInt( (int) myElement );
Однако отражение всегда может замедлить выполнение. Если вы не собираетесь создавать иерархию супер тяжелого класса с огромным количеством экземпляров - тогда этого должно быть достаточно для ваших нужд.