Dlaczego rzucenie dynamiki typu obiekt do obiektu rzuca wyjątek null reference?

Mam następującą funkcję:

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

Kiedy nazywam to w następujący sposób,

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

(T)boxed rzuca wyjątek null reference.

Każdy inny typ, który tam umieściłem, poza" obiektem", działa idealnie.
Jakieś pomysły, co to jest i dlaczego rzuca wyjątek?

Edytuj: Powodem, dla którego używam dynamic, jest unikanie WYJĄTKÓW podczas konwersji typów, na przykład:

double a = 123;
int v = TUtils.TryGetArrayValue<int>(new object[] { a }, 0);
Author: bedo, 2012-03-29

4 answers

Zgadzam się z innymi odpowiedzialnymi, którzy twierdzą, że to wygląda jak pluskwa. W szczególności wydaje się, że jest to błąd w warstwie wiążącej C# runtime, chociaż nie zbadałem go dokładnie.

Przepraszam za błąd. Zgłoszę to do zespołu testującego C# 5 i zobaczymy, czy zostało już zgłoszone i naprawione w C # 5. (Odtwarza się w ostatniej wersji beta, więc jest mało prawdopodobne, że został już zgłoszony i naprawiony.) Jeśli nie, jest mało prawdopodobne, aby poprawka trafiła do finalnego wydania. W takim przypadku rozważymy to dla ewentualnego zwolnienia serwisowego.

Dzięki za zwrócenie na to uwagi. Jeśli masz ochotę wprowadzić problem z połączeniem, aby go śledzić, nie krępuj się tego zrobić i dołącz link do tego pytania Stoskoverflow. Jeśli nie, nie ma problemu; zespół testowy będzie wiedział o tym tak czy inaczej.

 42
Author: Eric Lippert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-29 19:46:52

Jest to problem z tym, jak działa dynamic - binder runtime ma problem z konwersjami z System.Object, ale w praktyce tak naprawdę nie jest problemem.

Podejrzewam, że to dlatego, że dynamic, w czasie wykonywania, jest sobą zawsze System.Object. Specyfikacja języka C# w wersji 4.7 stwierdza: "Typ dynamic jest nieodróżnialny od obiektu w czasie wykonywania."Jako taki, każdy obiekt używany jako dynamiczny jest po prostu przechowywany jako obiekt.

Kiedy umieścisz rzeczywistą instancję System.Object w dynamice, coś jest występuje w rozdzielczości wiązania runtime, która powoduje wyjątek null reference.

Jednak każdy inny typ , który nie jest System.Object działa - nawet typy referencyjne i tym podobne, bez wad. W związku z tym powinno to zapewnić właściwe zachowanie, ponieważ naprawdę nie ma powodów, aby utworzyć instancję System.Object, która byłaby przekazywana - zawsze potrzebujesz podklasy z innymi informacjami o typie.

Jak tylko użyjesz dowolnego "prawdziwego" typu, działa to dobrze. Na exmaple, następujące prace, mimo że jest przekazywana i traktowana jako 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();
    }
}
 14
Author: Reed Copsey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-29 19:18:06

To jest naprawdę dziwne zachowanie i rzeczywiście wygląda jak błąd w implementacji dynamic. Odkryłem, że ta odmiana nie wyrzuca wyjątku i rzeczywiście zwraca obiekt:

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

Zauważ, że musiałem dodać ogólne ograniczenie w podpisie metody, ponieważ operator as działa tylko wtedy, gdy T jest typem odniesienia.

Jeśli szukasz obejścia, możesz użyć tego (I Wiem, że jest brzydki):

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;
}
 6
Author: Elian Ebbing,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-29 19:27:58

Ma to coś wspólnego ze słowem kluczowym dynamic. Jeśli zmienię Typ Na t dla pudełka to działa.

    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;

    }

Czy Jest jakiś szczególny powód, dla którego używasz dynamic? Naprawdę nie potrzebujesz go w tym przypadku, ponieważ wiesz, jaki typ jest wcześniej. Jeśli spojrzysz, w Twojej wersji Typ pudełka nie jest obiektowy, ale jest dynamiczny{obiekt} , co może być problemem podczas próby rzucenia na obiekt. Jeśli spojrzysz na tę wersję, którą opublikowałem, otrzymasz typ obiektu i brak błędów.

 2
Author: Jetti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-29 19:07:31