универсальное приведение из объектно-коробочного типа
Почему это работает:
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 ответа:
Можно только привести упакованные типы значений обратно к точному типу, который был упакован. Не имеет значения, существует ли неявное или явное преобразование из коробочного типа в тот, в который вы выполняете приведение - вам все равно нужно привести к коробочному типу (чтобы распаковать), а затем взять его оттуда.
В примере это означает два последовательных приведения:
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)); } }
Причина, по которой ваш код не работает, была хорошо объяснена другими участниками этой публикации.