ColorCast
@ColorCast
Человек атомоход

Cannot convert return expression of type, C# generic method, как вернуть конкретный тип?

Пишу парсер, принимающий на вход строку и возвращающий типизированные значения. Сигнатура метода задана тестами. Боксинг и анбоксинг запрещен.

public static class MyParser
   {
       public static T? Parse<T>(in string str)
       {
           if (typeof(T)==typeof(int))  return ParseInteger(str);
           if (typeof(T)==typeof(decimal))  return ParseDecimal(str);
           if (typeof(T)==typeof(DateTime))  return ParseDate(str, format);
           throw new ArgumentException();
       }
   }

Получаю ошибки:

Cannot convert expression type 'int' to return type 'T'
Cannot convert expression type 'decimal' to return type 'T'
Cannot convert expression type 'DateTime' to return type 'T'

Применение конструкций типа return Unsafe.As<T>() пустая затея.
В одном древнем топике нашел, что дело может быть в Resharper, но поскольку я пользуюсь Rider под Linux и Mono, то эту неприятность я отложил на потом.

Как можно вернуть конкретный тип значения без изменения сигнатуры?
  • Вопрос задан
  • 90 просмотров
Решения вопроса 2
AshBlade
@AshBlade Куратор тега C#
Просто хочу быть счастливым
как можно вернуть конкретный тип значения без изменения сигнатуры?

В данном случае - никак.
T - это generic параметр для подстановки во время компиляции. Он означает конкретный тип.
Так как никаких ограничений на него не выставлено, то считается, что может в него быть вставлено абсолютно что-угодно. Но возвращаемые значения должны уметь конвертироваться в этот возвращаемый тип, например, если T - long, то ParseInt сработает, т.к. int конвертируется к long (неявно и явно), а DateTime к long просто так нет. А теперь вместо T подставь byte или какой-нибудь класс и подумай - можно ли int к этому классу сконвертировать.

Пишу парсер

Когда я писал свой парсер, то делал базовый класс с абстрактным свойством, которое показывает что это за тип, и для каждого возможного значения - отдельный класс, который наследуется от базового. Чтобы понимать, что в результате пришло - смотрю на это поле.

Дополнительно, в данном случае лучше применять какую-нидудь фабрику этих парсеров, т.к. в рантайме через рефлексию определять свой тип и на основании него парсить - такое себе.

Например, так можно

enum TokenType
{
    Int,
    Decimal,
    DateTime,
}

public abstract class ParseResult
{
      public abstract TokenType Type { get; }
}

public class IntParseResult: ParseResult
{
       public override TokenType Type => TokenType.Int;
       public int Value { get; }
       IntParseResult(int value)
       {
             Value = value;
        }
}

public class DecimalParseResult: ParseResult
{
       public override TokenType Type => TokenType.Decimal;
       public decimal Value { get; }
       DecimalParseResult(decimal value)
       {
             Value = value;
        }
}

public class DateTimeParseResult: ParseResult
{
       public override TokenType Type => TokenType.DateTime;
       public DateTime Value { get; }
       DateTimeParseResult(DateTime value)
       {
             Value = value;
        }
}

public interface IParser
{
          ParseResult Parse(string str);
}

public class IntParser: IParser
{
        public ParseResult Parse(string str) => ParseInt(str);
}

public class DecimalParser: IParser
{
        public ParseResult Parse(string str) => ParseDecimal(str);
}

public class DateTimeParser: IParser
{
        public ParseResult Parse(string str) => ParseDateTime(str);
}

public static class MyParser
   {
       private static IParser GetParserFor(string str) { /* .... */ };
       public static ParseResult Parse(string str)
       {
              var parser = GetParserFor(str);
              return parser.Parse(str);
       }
   }


Я рекомендую убрать этот статический парсер и вынести решение о том какой парсер использовать выше по иерархии вызовов, либо пересмотреть логику парсинга, т.к. просто посмотреть на строку и понять как парсить - не самое эффективное решение.
Ответ написан
Комментировать
@mvv-rus
Настоящий админ AD и ненастоящий программист
T - это параметр-тип обобщенного метода. Как и все параметры, этот параметр задается извне, при вызове конкретной специализации обобщенного метода. И, кстати, компилятор вообще не может знать, что T может принадлежать к одному из трех перечисленных типов: их проверка, с выбрасыванием исключения, производится уже во время выполнения.
Так что изнутри метода, если вы хотите его сохранить, а не выкидывать, придется вам возвращать Object и разбираться с типом возвращенного значения уже после вызова. Причем, возврат Object вместо числового типа - это ещё и лишние накладные расходы на упаковку/распаковку (box/unbox). Так что я бы на вашем месте этот метод выкинул.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы