Как преобразовать int в double и наоборот при разборе с помощью num.parse?

Минимальный воспроизводимый код:

void main() {
  print(foo<int>('1'));
  print(foo<double>('1.0'));
  print(foo<double>('1'));
  print(foo<int>('1.0'));
}

T foo<T extends num>(String s) {
  final res = num.parse(s);
  return (T == double ? res.toDouble() : res.toInt()) as T;
}

Как видите, я вручную обрабатываю типы. Есть ли лучший способ сделать это?

@BrunoPeixoto Я не вижу ничего плохого в этом вопросе.

jamesdlin 18.01.2023 17:05

Ну, я недостаточно умен. Я вернусь в библиотеку.

Bruno Peixoto 18.01.2023 17:08

Интересно, в чем смысл этого упражнения? Вам все равно нужно включить параметр типа T из foo<T>() в исходный код, так почему бы не иметь отдельные функции для int и double?

Ber 18.01.2023 17:10

К сожалению, если вам нужен класс, который обрабатывает как int, так и double, я думаю, вам придется прибегнуть к этому. Однако вместо этого я бы использовал return (T == double ? double.parse(s) : int.parse(s)) as T;, чтобы foo<int>('3.9') генерировал ошибку, а не усекался молча (если только это поведение вам не нужно).

jamesdlin 18.01.2023 17:15

@Ber Да, было бы лучше, если бы это был вариант. Однако вы можете себе представить, что аналогичная проблема может возникнуть в нетривиальном универсальном классе.

jamesdlin 18.01.2023 17:19

@jamesdlin Ну, в таком случае я бы посчитал это действительно плохим дизайном. Весь смысл дженериков и наследования классов заключается в том, чтобы избежать проверки типов и особых случаев.

Ber 18.01.2023 17:38

@jamesdlin Спасибо за подтверждение. И да, мне нужно было заставить foo<int>('1.0') работать, поэтому я использовал toInt() там.

iDecode 18.01.2023 19:08

@Ber В общем, я бы согласился, но num немного раздражает, и есть только два возможных конкретных класса, поэтому я думаю, что частный случай приемлем.

jamesdlin 18.01.2023 19:08

@Ber Я не хочу создавать две функции, потому что у меня есть виджет, который принимает тип T extends num и возвращает значение T после вызова foo.

iDecode 18.01.2023 19:10

@iDecode Вы можете получить два класса виджетов, по одному для каждого типа. Или вы передаете соответствующую версию foo в качестве аргумента.

Ber 18.01.2023 22:43

@Ber, если я получу два класса виджетов и помещу логику в суперкласс, тогда я все равно буду иметь дело с num. Извините, я не получил вас, передав соответствующую версию foo.

iDecode 19.01.2023 13:50

@iDecode Возможно, это вопрос, который слишком сосредоточен на конкретном решении более абстрактной проблемы. Если вы зададите вопрос о дизайне своих виджетов, возможно, вы получите гораздо лучший ответ.

Ber 20.01.2023 12:49
1
12
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я не вижу лучшего решения. У вас есть функция, которая полностью меняет поведение на основе аргумента типа. Поскольку все, что вы можете сделать с аргументом типа, — это проверить подтип или сравнить его с литералом постоянного типа, вам нужно сделать что-то подобное.

Я бы предпочел проверку подтипа, потому что это также позволяет продвигать, но для чего-то ограниченного, такого как это, где есть только четыре возможных типа для T, проверка Type на равенство объектов также может работать. Перед возвращением должен быть хотя бы один as T.

Любой подход также работает только для иерархий типов, которые являются конечными, и вы учитываете все возможные типы. Даже здесь текущий код не охватывает <num> и <Never>, которые также являются допустимыми аргументами типа для связанного num. Итак, будьте бдительны.

Возможно, используя проверки подтипа для продвижения:

T parse<T extends num>(String source) {
  var value = num.parse(source);
  if (value is T) return value;
  // T is not `num`.
  num d = value.toDouble();
  if (d is T) return d; // T was double.
  try {
    num n = value.toInt(); // Can fail for Infinity/NaN
    if (n is T) return n; // T was int
  } catch (_) {
    // T was `int` after all, so throwing was correct.
    if (1 is T) rethrow; // T was int, but the input was not valid.
  }
  // T was neither num, double, nor int, so must be `Never`.
  throw ArgumentError.value(T, "T", "Must not be Never");
}

Или. используя Type равенство объектов:

T parse<T extends num>(String source) {
  var value = num.parse(source);
  switch (T) {
    case num: return value as T;
    case int: return value.toInt() as T;
    case double: return value.toDouble() as T;
    default: 
     throw ArgumentError.value(T, "T", "Must not be Never");
  }
}

Другие вопросы по теме