Подтвердить что ты не робот

'Delegate' System.Action 'не принимает 0 аргументов.' Является ли это ошибкой компилятора С# (lambdas + два проекта)?

Рассмотрим приведенный ниже код. Похоже, что действительно правильный код на С#?

//Project B
using System;
public delegate void ActionSurrogate(Action addEvent);
//public delegate void ActionSurrogate2();
// Using ActionSurrogate2 instead of System.Action results in the same error
// Using a dummy parameter (Action<double, int>) results in the same error

// Project A
public static class Class1 {
    public static void ThisWontCompile() {
        ActionSurrogate b = (a) =>
                            {
                                a(); // Error given here
                            };
    }
}

Я получаю ошибку компилятора. "Делегировать действие" не принимает 0 аргументов. в указанной позиции с использованием компилятора (Microsoft) С# 4.0. Обратите внимание, что вы должны объявить ActionSurrogate в другом проекте для проявления этой ошибки.

Это становится более интересным:

// Project A, File 1
public static class Class1 {
    public static void ThisWontCompile() {
        ActionSurrogate b = (a) => { a(); /* Error given here */ };
        ActionSurrogate c = (a) => { a(); /* Error given here too */ };
        Action d = () => { };
        ActionSurrogate c = (a) => { a(); /* No error is given here */ };
    }
}

Я наткнулся на ошибку компилятора С# здесь?

Обратите внимание, что это довольно раздражающая ошибка для тех, кто любит использовать lambdas много и пытается создать библиотеку структур данных для будущего использования... (me)

РЕДАКТИРОВАТЬ: удаленный хрупкий случай.

Я скопировал и раздели мой первоначальный проект до минимума, чтобы это произошло. Это буквально весь код в моем новом проекте.

4b9b3361

Ответ 1

Вероятно, это проблема с типом вывода, поэтому компилятор передает a как Action<T> вместо Action (может показаться, что a is ActionSurrogate, который соответствует подписи Action<Action>>), Попробуйте явно указать тип a:

    ActionSurrogate b = (Action a) =>
                        {
                            a();
                        };

Если это не так - можете проверить вокруг вашего проекта для любых самоопределяемых делегатов Action, принимающих один параметр.

Ответ 2

ЗАКЛЮЧИТЕЛЬНОЕ ОБНОВЛЕНИЕ:

Исправлена ​​ошибка в С# 5. Извинитесь за неудобства и благодарность за отчет.


Исходный анализ:

Я могу воспроизвести проблему с помощью компилятора командной строки. Это, безусловно, похоже на ошибку. Это, наверное, моя ошибка; Извини за это. (Я написал весь код проверки конверсии лямбда-делегирования.)

Сейчас я в кафе, и у меня нет доступа к источникам компилятора. Я попытаюсь найти некоторое время, чтобы воспроизвести это в сборке отладки завтра и посмотреть, смогу ли я решить, что происходит. Если я не найду времени, я выйду из офиса до Рождества.

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

Спасибо за отчет!

ОБНОВЛЕНИЕ: Я сейчас на автобусе, и он просто пришел ко мне; Кажется, я точно знаю, что не так. Компилятор ленив, особенно при работе с типами, которые поступают из метаданных. Причина в том, что в ссылочных сборках могут быть сотни тысяч типов, и нет необходимости загружать информацию обо всех них. Вы собираетесь использовать гораздо меньше, чем 1% из них, вероятно, так что не тратьте много времени и памяти на вещи, которые вы никогда не собираетесь использовать. На самом деле лень идет глубже, чем это; тип проходит через несколько "стадий", прежде чем он может быть использован. Сначала его имя известно, затем его базовый тип, то есть ли его базовая иерархия типов является обоснованной (ацикличной и т.д.), Затем ее ограничениями параметра типа, а затем ее членами, то являются ли члены обоснованными (что переопределяет что-то одной и той же сигнатуры и т.д.) Я готов поспорить, что логика преобразования не может вызвать метод, который говорит: "Убедитесь, что типы всех параметров делегата известны своим членам", прежде чем он проверит подпись делегата вызывать для совместимости. Но код, который делает локальную переменную, вероятно, делает это. Я думаю, что во время проверки конверсии тип действия может даже не иметь метод invoke в отношении компилятора.

Мы скоро узнаем.

ОБНОВЛЕНИЕ: Мои психические силы сегодня сильны. Когда разрешение перегрузки пытается определить, существует ли метод Invoke для типа делегата, который принимает нулевые аргументы, он находит нулевые методы Invoke на выбор. Мы должны гарантировать, что метаданные типа делегата будут полностью загружены, прежде чем мы сделаем перегрузку. Как странно, что это осталось незамеченным так долго; он воспроизводится на С# 3.0. Конечно, он не воспроизводится на С# 2.0 просто потому, что не было лямбда; анонимные методы в С# 2.0 требуют, чтобы вы указывали тип явно, что создает локальный, который, как мы знаем, загружает метаданные. Но я бы предположил, что основная причина ошибки - это разрешение перегрузки не приводит к загрузке метаданных для вызова - возвращается к С# 1.0.

В любом случае, увлекательная ошибка, спасибо за отчет. Очевидно, у вас есть обходной путь. У меня будет QA отслеживать это отсюда, и мы постараемся сделать это исправлено для С# 5. (Мы пропустили окно для Service Pack 1, который уже в бета-версии.)

Ответ 3

    public static void ThisWontCompile()
        {
            ActionSurrogate b = (Action a) =>
            {
                a();
            };


        }

Это скомпилируется. Некоторая сбой с компилятором не позволяет найти делегата Action без параметров. Вот почему вы получаете ошибку.

public delegate void Action();
public delegate void Action<T>();
public delegate void Action<T1,T2>();
public delegate void Action<T1,T2,T3>();
public delegate void Action<T1,T2,T3,T4>();