Генерични аргументи Автоматично дефиниране в методи

Имам обща дефиниция на команда с един аргумент, който връща някаква стойност

interface ICommand<Targ, TResult> {
    TResult Run(Targ argument);    
}

И имам интерпретатор с общ метод за този тип команди

class Interpreter{
  public TResult Run<TCommand, TArg, TResult>(TArg arg) 
      where TCommand: ICommand<TArg, TResult>, new()
  {
      var cmd = new TCommand();
      return cmd.Run(arg);
  }

Така че наричам тези команди по такъв начин

var interpreter = new Interpreter();
double converted = interpreter.Run<ConvertCommand, string, double>("123.5");

където

ConvertCommand: ICommand<string, double>

Но искам да стартирам тези команди по минималистичен начин

var interpreter = new Interpreter();
double converted = interpreter.Run<ConvertCommand>("123.5");

Изглежда, че има достатъчно информация за общ тип за CLR за компилиране на това, но не иска.

Има ли някакъв начин да стартирате този тип общи методи с един общ аргумент?


person tmt    schedule 26.01.2016    source източник


Отговори (1)


Е, изводът за общ тип изисква аргументите на метода да предоставят типа за аргументите, за да позволи на компилатора да изведе техните общи типове.

Така че само TArg може да бъде изведено, докато TResult не. В обобщение: не, не можете да извикате общ метод, без да предоставите всички общи аргументи.

Възможна алтернатива: преработете кода си, както следва...

Вярвам, че мога да ви дам проект на това, което бих сметнал за по-добър дизайн във вашия случай. Проверете кода и по-късно ще го обясня по-долу в примера:

public interface ICommand<TArg, TResult>
{
    TArg Arg { get; set; }
    TResult Run();
}

public sealed class ConvertCommand : ICommand<string, double>
{
    public string Arg { get; set; }

    public double Run()
    {
        return Convert.ToDouble(Arg);
    }
}

public static class CommandFactory
{
    public static TCommand Create<TCommand, TArg, TReturnValue>(TArg arg)
        where TCommand : ICommand<TArg, TReturnValue>, new ()
    {
        var cmd = new TCommand();
        cmd.Arg = arg;
        return cmd;
    }

    // This is like a shortcut method/helper to avoid providing 
    // generic parameters that can't be inferred...
    public static ConvertCommand ConvertCommand(string arg)
    {
        return Create<ConvertCommand, string, double>(arg);
    }
}

class Interpreter
{
    public TReturnValue Run<TArg, TReturnValue>(ICommand<TArg, TReturnValue> cmd)
    {
        return cmd.Run();
    }
}

Мисля, че не трябва да делегирате отговорността за изграждане на команди на интерпретатора, но той трябва да бъде фабрика за команди. Тоест, интерпретаторът просто изпълнява командата, получава резултатите от командата, обработва ги, излъчва събития... това вече зависи от вас.

От друга страна, проверете дали методът Interpreter.Run получава ICommand<TArg, TReturn> вместо параметър за общ тип. По този начин **както TArg, така и TReturn аргументи от общ тип могат да бъдат изведени от ICommand<TArg, TReturn> от неговата реализация и не е необходимо да предоставяте изрично тези общи аргументи:

Interpreter interpreter = new Interpreter();
// TArg and TReturn are inferred!!
interpreter.Run(CommandFactory.ConvertCommand("0.1"));

Всъщност бих избрал други дизайни/архитектури в сценарий от реалния свят, но исках да ви подскажа как да преработите действителната си идея и да я оставите да работи, както очаквате;)

person Matías Fidemraizer    schedule 26.01.2016
comment
но имам тази информация в IComand‹T1,T2› дефиниция. Няма как да го използвам? - person tmt; 26.01.2016
comment
@tmt Не. Не можете частично да изведете общи аргументи. Можете да накарате компилатора да изведе всички или нито един от тях. - person D Stanley; 26.01.2016
comment
Разбира се, Thx. Това е, което се опитах да избегна. вместо мръсна 3-редова конструкция с използване на мръсен синтаксис за създаване (new() или, factory). Но животът е несъвършен. Почти като C#... - person tmt; 26.01.2016
comment
@tmt Е, C# е строго типизиран език. Ако не можете да гарантирате писане по време на компилиране, C# не може да отгатне какво имате в ума си. Вярвам, че това е един от най-мощните налични езици днес и има впечатляващ набор от функции. IMHO, има няколко случая, в които C# е ограничението: повечето пъти това е повече за нашия ум: имаме набор от инструменти и трябва да изградим софтуер, базиран на инструмента. Понякога въображението надхвърля всичко възможно :D - person Matías Fidemraizer; 27.01.2016
comment
@MatíasFidemraizer Да, # е най-добрият. Този пример съдържа пълни видове информация. Тъй като не е необходимо да указвате общи аргументи при извикване на linq (където указването на обект е аргумент) - също има пълна информация за типовете, но при извикване на общ тип първи аргумент. Виждам - ​​няма езикови конструкции в c#, ​​които ни позволяват да извикаме някои генерични средства по кратък начин, ако всички генерични аргументи са свързани, но на теория изглежда възможно. Пропуснал ли съм нещо? - person tmt; 28.01.2016
comment
@tmt По-трудно е, отколкото си представяте... Някои човешки предположения са трудни за прилагане и обхващат всеки случай, без да позволяват опасни практики за програмиране... - person Matías Fidemraizer; 28.01.2016