Можно ли вернуть общее значение при преобразовании из строки?

У меня есть система, которая ретранслирует стороннее ПО, в котором хранятся элементы данных.

Элементы данных в хранилище имеют свойства DataType и Value для описания подчеркнутого объекта данных. Каждый DataElement можно получить по имени из хранилища. (как внешняя база данных)

public class DataElemenet
{
    public string Type { get; }
    public string Value { get; }
}

Я хочу создать объект BL, чтобы абстрагироваться от особенностей DataElement следующим образом:

public class DataGetter
{
    private readonly DataElement dataObject;
    public DataGetter(string name)
    {
        this.dataObject = <third_party>.GetObject(name);
    }

    public int GetValue()
    {
        if (this.dataObject.Type != "integer")
           throw new Exception();
        
        return int.Parse(this.dataObject.Value);
    }
}

Теперь в стороннем ПО Type может быть разных типов (например, List<int>, double, List<string>....)

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

public T GetValue<T>()
{
    if (typeof(T) != this.dataObject.Type)
       throw new Exception();
    
    return (T)Convert.ChangeType(this.dataObject.Value, typeof(T));
}

Хотя этот подход будет работать, я хочу оптимизировать его следующим образом (обрабатывать все типы данных в каждом конкретном случае): РЕДАКТИРОВАТЬ

Как упоминалось в одном из комментариев, этот подход работает только для примитивов и не может поддерживать преобразование, например, в List<int>. Поэтому я попытался использовать следующий подход:

public T GetValue<T>()
{
    if (typeof(T) != this.dataObject.Type)
       throw new Exception();

    if (typeof(T) == typeof(int))
       return int.Parse(this.dataObject.Value);
    else if (typeof(T) == typeof(bool))
       return bool.Parse(this.dataObject.Value);
    ....

}

Компилятор жалуется, что не может преобразовать int / bool (в приведенных выше случаях) в T.

Можно ли это сделать?

Я не понимаю проблемы, если у вас есть решение. Запрашивать оптимизацию — это просить мнения и не по теме.

possum 10.01.2023 13:28

Весь смысл универсального метода в том, что вы делаете одно и то же для каждого типа. Если вам нужно использовать условные операторы для проверки типа и делать что-то другое для каждого из них, вам не следует использовать дженерики в первую очередь.

jmcilhinney 10.01.2023 13:28

Как вы думаете, почему это будет оптимизация по сравнению с Convert.ChangeType?

Magnus 10.01.2023 13:32

Вы говорите, что этот первый общий метод будет работать, но так ли это на самом деле? Вы тестировали его со всеми теми различными типами, которые вы упомянули? В документации для Convert.ChangeType говорится следующее: «Для успешного преобразования значение должно реализовывать интерфейс IConvertible, потому что метод просто переносит вызов в соответствующий метод IConvertible. Метод требует поддержки преобразования значения в convertType». Как можно преобразовать string в List<int> в таких условиях?

jmcilhinney 10.01.2023 13:32

Обратите внимание, что вызывающая сторона должна указать T в этом случае (не может быть выведена), поэтому вызывающей стороне придется делать GetValue<int> и даже такие уродливые вещи, как GetValue<List<string>>. Еще хуже то, что он может предоставлять неподдерживаемый тип. Все это заставляет меня думать, что лучше просто использовать методы GetInt, GetBool и т. д.

Evk 10.01.2023 13:35

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

NirMH 10.01.2023 13:43

Затем вы также можете добавить object Get(), который преобразует Value в тип Type и возвращает как объект. В основном этот дизайн будет таким же, как SqlDataReader должен читать столбцы строки, предоставленной базой данных (у нее есть GetInt32 GetBoolean и т. д. и т. д.): learn.microsoft.com/en-us/dotnet/api/…

Evk 10.01.2023 13:57
Типы данных JavaScript
Типы данных JavaScript
В JavaScript существует несколько типов данных, включая примитивные типы данных и ссылочные типы данных. Вот краткое объяснение различных типов данных...
Как сделать движок для футбольного матча? (простой вариант)
Как сделать движок для футбольного матча? (простой вариант)
Футбол. Для многих людей, живущих на земле, эта игра - больше, чем просто спорт. И эти люди всегда мечтают стать футболистом или менеджером. Но, к...
Знайте свои исключения!
Знайте свои исключения!
В Java исключение - это событие, возникающее во время выполнения программы, которое нарушает нормальный ход выполнения инструкций программы. Когда...
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик Модуль flexbox, также известный как гибкий модуль разметки box, помогает эффективно проектировать и...
Введение в раздел &quot;Заголовок&quot; в HTML
Введение в раздел "Заголовок" в HTML
Говорят, что лучшее о человеке можно увидеть только изнутри, и это относится и к веб-страницам HTML! Причина, по которой некоторые веб-страницы не...
0
7
61
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Да, это возможно: сначала вам нужно выполнить приведение к object (в этом случае помните о боксе/распаковке).

public T GetValue<T>()
{
    if (typeof(T) != this.dataObject.Type)
       throw new Exception();

    if (typeof(T) == typeof(int))
       return (T)(object)int.Parse(this.dataObject.Value);
    else if (typeof(T) == typeof(bool))
       return (T)(object)bool.Parse(this.dataObject.Value);
    ....

}

ДЕМО

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

Вы можете положиться на новый интерфейс IParsable<T> из .NET7, который имеет общий статический метод Parse, который вы можете использовать в общих контекстах.

Сохраняя исходную попытку, это может выглядеть примерно так:

public T GetValue<T>()
    where T : IParsable<T>
{
    if (typeof(T) != this.dataObject.Type)
       throw new Exception();
    
    return T.Parse(this.dataObject.Value, CultureInfo.InvariantCulture);
}

Обратите внимание, что тип, который вы сейчас передаете, должен реализовывать статический абстрактный метод Parse из IParsable, что делает подавляющее большинство стандартных примитивных и базовых типов. Если вы затем захотите поддерживать такие вещи, как произвольные списки из строки, вы можете создать свои собственные пользовательские коллекции, которые реализуют IParsable, и поместить туда преобразование из строки в список.

Преимущество этого подхода в том, что он действительно полиморфен: он не заставляет вас проверять все возможные типы в гигантском операторе switch. Таким образом, он значительно более ремонтопригоден и расширяем.

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