Явный оператор приведения применяется к экземпляру, созданному с помощью отражения

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

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 ответа

Решение

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

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

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;

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

Вы создали B, И затем брось это A,

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

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

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

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

Для того, чтобы:

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

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

Другие вопросы по тегам