Почему я получаю эту ошибку компиляции при попытке вызвать базовый конструктор / метод, который принимает динамический аргумент? - PullRequest
45 голосов
/ 11 ноября 2011

При рефакторинге некоторого кода я наткнулся на странную ошибку компиляции:

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

Кажется, это происходит при попытке вызвать базовые методы / конструкторы, которые принимают динамические аргументы. Например:

class ClassA
{
    public ClassA(dynamic test)
    {
        Console.WriteLine("ClassA");
    }
}

class ClassB : ClassA
{
    public ClassB(dynamic test)
        : base(test)
    {
        Console.WriteLine("ClassB");
    }
}

Это работает, если я приведу аргумент к object, вот так:

public ClassB(dynamic test)
    : base((object)test)

Итак, я немного растерялся. Почему я должен вставить этот неприятный приведение - почему компилятор не может понять, что я имею в виду?

1 Ответ

43 голосов
/ 11 ноября 2011

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

РЕДАКТИРОВАТЬ: В "нормальном" коде C # (в основном, до C # 4), все разрешение перегрузки выполняется во время компиляции.Однако, когда вызов члена включает динамическое значение, которое разрешается во время выполнения.Например, рассмотрим следующее:

using System;

class Program
{
    static void Foo(int x)
    {
        Console.WriteLine("int!");
    }

    static void Foo(string x)
    {
        Console.WriteLine("string!");
    }

    static void Main(string[] args)  
    {
        dynamic d = 10;
        Foo(d);
    }
}

Компилятор здесь не отправляет прямой вызов Foo - не может, потому что при вызове Foo(d) он не знает, какая перегрузка будетразрешитьВместо этого он генерирует код, который выполняет своего рода мини-компиляцию «точно в срок», чтобы разрешить перегрузку с типом фактическим значения d во время выполнения.

Теперь, когдане работает для цепочки конструктора, так как действительный IL должен содержать вызов определенного конструктора базового класса.(Я не знаю, может ли динамическая версия даже быть выраженной в IL, или может ли она быть, но результат будет непроверенным.)

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

...