общее литье из объектно-боксового типа

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

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);

Второй пример выдает:

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

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

public T GetValue(string q)

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

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


person Manolo    schedule 22.11.2013    source источник


Ответы (3)


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

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

double dd = (double) (decimal) o;

Или с помощью методов Convert:

double dd = Convert.ToDouble(o);

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

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

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

person Jon    schedule 22.11.2013

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

double dd = (double) (decimal) o;

Обратите внимание, что первый (decimal) распаковывается, (double) отливается.

person King King    schedule 22.11.2013

Это можно сделать с помощью 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));
    }
}

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

person Mike Perrenoud    schedule 22.11.2013