Система "Делегирования".Действие "не принимает 0 аргументов". Это ошибка компилятора C # (лямбды + два проекта)?

StackOverflow https://stackoverflow.com/questions/4466859

Вопрос

Рассмотрим приведенный ниже код.Выглядит как совершенно корректный код на C #, не так ли?

//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) C # 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 */ };
    }
}

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

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

Редактировать:удален ошибочный регистр.

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

Это было полезно?

Решение

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

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

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

Другие советы

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

Ошибка была исправлена в C # 5.Еще раз приношу извинения за доставленные неудобства и благодарю за отчет.


Оригинальный анализ:

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

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

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

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

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

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

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

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

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


        }

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

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>();
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top