Преобразовать строку в зависимости от ее признака в БД в простые типы данных C#

Вытаскиваю с БД таблицу в которой есть |id|Code|Type|Value| в модель:

public record MyDto
{
    public Guid Id{ get; set; }
    public Code Code { get; set; }
    public Type Type { get; set; }
    public string Value { get; set; }
}

Необходимо по признаку |Type| вернуть из метода соответствующий тип данных лежащий в поле Value которая парситься всегда в строку, но ее содержимое может быть например "100" или "Привет Мир!". Я ничего лучшего пока не придумал, но очень сомнительный код:

 public async Task<object> GetDataByCodeAsync(Code code, CancellationToken token)
    {
        var result = await _repository.GetDataByCodeAsync(code, token);

        switch (result.Type)
        {
            case Type.String:
                return result.Value.ToString();
            case Type.Decimal:
                return Convert.ToDecimal(result.Value);
            case Type.Integer:
                return Convert.ToInt32(result.Value);
            case Type.Guid:
                return Guid.Parse(result.Value);
            default:
                throw new ArgumentOutOfRangeException();
        }
    }

Можно пока без проверок или TryParse, просто сугубо понимать, как реализовать в нормальном виде.


Ответы (1 шт):

Автор решения: Alexander Petrov

Можно применить паттерн-матчинг для лаконичности:

return result.Type switch
{
    SettingType.String => result.Value,
    SettingType.Decimal => decimal.Parse(result.Value),
    SettingType.Integer => int.Parse(result.Value),
    SettingType.Guid => Guid.Parse(result.Value),
    _ => throw new ArgumentOutOfRangeException()
};

Давайте разберёмся.
Ваш метод GetDataByCodeAsync возвращает какое-то значение. Что дальше с ним происходит? Очевидно, оно как-то обрабатывается. И, вероятно, обрабатывать нужно по разному в зависимости от типа.

Мне много раз попадались такие вопросы с конвертацией типа из строки и дальнейшим его использованием. На предложения показать проблему более широко практически всегда авторы упираются и хотят непременно чёрную магию: чтобы оно само как-то работало...

В тех задачах, что я видел, обычно код выглядит примерно так:

object value = GetDataByCode(...);
Process(value);

Внутри Process опять приходится определять, какой именно тип имеет параметр value: if (value is int n) ... и т. п. То есть от чего уходили, к тому и вернулись. Разница лишь в том, что теперь тип задан не строкой.

Предлагаю следующее.

public async Task ProcessDataByCodeAsync(Code code, CancellationToken token = default)
{
    var result = await _repository.GetDataByCodeAsync(code, token);

    switch (result.Type)
    {
        case SettingType.String:
            Process(result.Value);
            break;
        case SettingType.Decimal:
            Process(decimal.Parse(result.Value));
            break;
        case SettingType.Integer:
            Process(int.Parse(result.Value));
            break;
        case SettingType.Guid:
            Process(Guid.Parse(result.Value));
            break;
        default:
            throw new ArgumentOutOfRangeException();
    }
}

И несколько перегрузок метода, принимающих нужный тип:

void Process(string value) { }
void Process(decimal value) { }
void Process(int value) { }
void Process(Guid value) { }

Таким образом наш метод выполняет диспетчеризацию в одном-единственном месте. А дальше мы без проблем работаем с конкретными типами.

→ Ссылка