явный оператор приведения применяется к экземпляру, созданному с помощью отражения - PullRequest
4 голосов
/ 02 августа 2011

Я был удивлен, когда обнаружил, что следующий код выдает исключение во время выполнения:

class A
{
    public string Name { get; set; }

    public A()
    {
        Name = "Class A";
    }
}

class B
{
    public string Name { get; set; }

    public B()
    {
        Name = "Class B";
    }

    public static explicit operator A(B source)
    {
        return new A() {Name = source.Name};
    }
}


class Program
{
    static void Main(string[] args)
    {
        // This executes with no error
        var bInstance = new B();
        Console.WriteLine(bInstance.GetType()); // <assemblyname>.B
        var aInstance = (A) bInstance;
        Console.WriteLine(aInstance.Name); // Class B

        // This fails with InvalidCastException
        var bInstanceReflection = Activator.CreateInstance(typeof (B));
        Console.WriteLine(bInstanceReflection.GetType()); // <assemblyname>.B
        var aInstanceReflection = (A) bInstanceReflection;

        Console.WriteLine(aInstanceReflection.Name);
    }
}

Может кто-нибудь сказать мне, почему? Я не очень понимаю, что случилось

Ответы [ 3 ]

7 голосов
/ 02 августа 2011

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

Когда мы удаляем неявную типизацию из кода, она становится немного понятнее:

object bInstanceReflection = Activator.CreateInstance(typeof (B));
Console.WriteLine(bInstanceReflection.GetType()); // <assemblyname>.B
A aInstanceReflection = (A) bInstanceReflection;

Теперь достаточно ясно, что в последней строке (A) - это просто приведение от object, которое выполняет обычное преобразование ссылок. Пользовательские преобразования не будут применяться вообще.

Если вы используете .NET 4, вы можете использовать динамическую типизацию, чтобы заставить его работать:

// Note the change of type
dynamic bInstanceReflection = Activator.CreateInstance(typeof (B));
Console.WriteLine(bInstanceReflection.GetType()); // <assemblyname>.B
A aInstanceReflection = (A) bInstanceReflection;

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

3 голосов
/ 02 августа 2011

Вы создали B. А затем приведите его к A.

Несмотря на наличие похожих макетов, B не имеет никакого отношения к A. Статические операторы применяются компилятором , но не во время выполнения посредством приведения. Хотя синтаксис C # один и тот же, они сильно отличаются при работе с отражением.

Это нормальное, ожидаемое поведение.

1 голос
/ 09 сентября 2011

Вы можете просто изменить эту строку:

var bInstanceReflection = Activator.CreateInstance(typeof (B));

Кому:

var bInstanceReflection = (B)Activator.CreateInstance(typeof (B));

Таким образом, компилятор теперь знает тип bInstanceReflection и может вызвать правильный имплитический оператор.

...