Почему приведение динамического объекта типа к объекту вызывает исключение нулевой ссылки? - PullRequest
63 голосов
/ 29 марта 2012

У меня есть следующая функция:

public static T TryGetArrayValue<T>(object[] array_, int index_)
{
    ... //some checking goes up here not relevant to question

    dynamic boxed = array_[index_];
    return (T)boxed;
}

Когда я вызываю ее следующим образом,

object a = new object();
object v = TUtils.TryGetArrayValue<object>(new object[] { a }, 0);

(T)boxed выдает исключение нулевой ссылки.

Любой другой тип, который я вставил туда, кроме "объекта", он прекрасно работает.
Есть идеи, что это такое и почему возникает исключение?

Редактировать: Причинапочему я использую динамический, чтобы избежать исключения при преобразовании типов, например:

double a = 123;
int v = TUtils.TryGetArrayValue<int>(new object[] { a }, 0);

Ответы [ 4 ]

43 голосов
/ 29 марта 2012

Я согласен с другими ответчиками, которые говорят, что это похоже на ошибку.В частности, это, похоже, ошибка в слое связывания во время выполнения C #, хотя я не исследовал ее полностью.

Я прошу прощения за ошибку.Я сообщу об этом команде по тестированию C # 5, и мы увидим, было ли это уже сообщено и исправлено в C # 5. (Он воспроизводится в последней бета-версии, поэтому маловероятно, что он уже был сообщен и исправлен.Если это не так, исправление вряд ли попадет в финальную версию.В этом случае мы рассмотрим его для возможного сервисного выпуска.

Спасибо, что сообщили об этом.Если вам нужно ввести проблему Connect * , чтобы отследить ее, не стесняйтесь сделать это и добавьте ссылку на этот вопрос StackOverflow.Если нет, нет проблем;команда тестирования узнает об этом в любом случае.

14 голосов
/ 29 марта 2012

Это проблема, связанная с тем, как работает динамическая система: средство связывания во время выполнения имеет проблему с преобразованиями из System.Object, но на практике это не проблема.

Я подозреваю, что это потому, что dynamic,во время выполнения само по себе всегда System.Object.Спецификация языка C # в 4.7 гласит: «Динамический тип неотличим от объекта во время выполнения».Таким образом, любой объект, используемый как динамический, просто сохраняется как объект.

Когда вы помещаете фактический экземпляр System.Object в динамический объект, в разрешении привязки времени выполнения происходит что-то, что вызывает исключение нулевой ссылки.

Однако любой другой тип , который не System.Object, работает - даже ссылочные типы и тому подобное, без недостатков.Таким образом, это должно обеспечить вам правильное поведение, поскольку на самом деле нет никаких причин создавать экземпляр самого System.Object, который бы передавался по кругу - вам всегда нужен некоторый подкласс с другой информацией о типах.

Как только вы используете какой-либо «настоящий» тип, это прекрасно работает.Например, следующие работы, даже если они переданы и обработаны как Object:

public class Program
{
    public static T TryGetArrayValue<T>(object[] array_, int index_)
    {

        dynamic boxed = array_[index_];
        return (T)boxed;
    }

    private static void Main()
    {
        int p = 3;
        object a = p;
        var objects = new[] { a, 4.5 };

        // This works now, since the object is pointing to a class instance
        object v = TryGetArrayValue<object>(objects, 0);
        Console.WriteLine(v);

        // These both also work fine...
        double d = TryGetArrayValue<double>(objects, 1);
        Console.WriteLine(d);
        // Even the "automatic" int conversion works now
        int i = TryGetArrayValue<int>(objects, 1);
        Console.WriteLine(i);
        Console.ReadKey();
    }
}
6 голосов
/ 29 марта 2012

Это действительно странное поведение, и оно действительно похоже на ошибку в реализации dynamic.Я обнаружил, что этот вариант не генерирует исключение и действительно возвращает объект:

public static T TryGetArrayValue<T>(object[] array, int index) where T : class
{
    dynamic boxed = array[index];
    return boxed as T;
}

Обратите внимание, что мне пришлось добавить общее ограничение в сигнатуру метода, потому что оператор as работает, только если Tэто ссылочный тип.

Если вы ищете обходной путь, вы можете использовать это (и я знаю, что это некрасиво):

public static T TryGetArrayValue<T>(object[] array, int index)
{
    dynamic boxed = array[index];

    if (typeof(T) == typeof(object))
        return (T)(boxed as object);

    return (T)boxed;
}
2 голосов
/ 29 марта 2012

Это как-то связано с динамическим ключевым словом. Если я поменяю тип на T в штучной упаковке, это сработает.

    static void Main(string[] args)
    {
        object a = new object();
        object v = TryGetArrayValue<object>(new object[] { a }, 0);

        Console.ReadLine();
    }

    public static T TryGetArrayValue<T>(object[] array_, int index_)
    {

            T boxed = (T)array_[index_];
            return boxed;

    }

Есть ли конкретная причина, по которой вы используете динамический? Вам действительно не нужно в этом случае, так как вы знаете, какой тип опережает время. Если вы посмотрите, в вашей версии тип в штучной упаковке не является объектом, но это динамический {объект}, который может быть проблемой при попытке привести к объекту. Если вы посмотрите на эту версию, которую я разместил, вы получите тип объекта и никаких ошибок.

...