Общие аргументы Автоопределение в методах

У меня есть общее определение команды с одним аргументом, который возвращает некоторое значение

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
Конечно, спасибо. Это то, чего я пытался избежать. вместо грязной трехстрочной конструкции с использованием грязного синтаксиса создания (new() или factory ). Но жизнь несовершенна. Почти как С#... - person tmt; 26.01.2016
comment
@tmt Ну, C # - это строго типизированный язык. Если вы не можете гарантировать типизацию во время компиляции, 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