универсальное приведение из объектно-коробочного типа


Почему это работает:

decimal dec = new Decimal(33);
double dd = (double) dec;
Console.WriteLine(dd);

Но не это:

decimal dec = new Decimal(33);
object o = (object)dec;
double dd = (double) o;
Console.WriteLine(dd);

Второй пример бросает:

Система.InvalidCastException: указанное приведение недопустимо.

Этот вопрос исходит из ситуации, когда у меня есть общий метод

public T GetValue(string q)

, который получает значения из источника данных. Типы этих значений неизвестны, но метод предполагает, что он может привести значение к T. иногда значение будет object{decimal} и T будет двойным, в этом случае будет выброшено исключение InvalidCastException. Но в принципе это не должно быть проблемой, так как значение является десятичным (хотя и заключенным в рамку объектом), которое может быть приведено к удвоению.

Как мне справиться с этим в целом?

3 4

3 ответа:

Можно только привести упакованные типы значений обратно к точному типу, который был упакован. Не имеет значения, существует ли неявное или явное преобразование из коробочного типа в тот, в который вы выполняете приведение - вам все равно нужно привести к коробочному типу (чтобы распаковать), а затем взять его оттуда.

В примере это означает два последовательных приведения:

double dd = (double) (decimal) o;

Или, используя Convert методы:

double dd = Convert.ToDouble(o);

Конечно, это не подходит для вашего реального случая использования. потому что вы не можете сразу перейти от параметра универсального типа к ToDouble. Но пока целевой Тип IConvertible, Вы можете сделать следующее:

double dd = (double)Convert.ChangeType(o, typeof(double));

, где параметр универсального типа T может быть заменен на double.

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

double dd = (double) (decimal) o;
Обратите внимание, что первый (decimal) - это распаковка, а второй (double) - литье.

Это можно сделать с помощью Convert.ChangeType:

class Program
{
    static void Main(string[] args)
    {
        decimal dec = new Decimal(33);
        object o = (object)dec;
        double dd = GetValue<double>(o);
        Console.WriteLine(dd);
    }

    static T GetValue<T>(object val)
    {
        return (T)Convert.ChangeType(val, typeof(T));
    }
}

Причина, по которой ваш код не работает, была хорошо объяснена другими участниками этой публикации.