Тип аргумента 'void' не присваивается типу параметра 'System.Action'

Это мой тестовый код:

class PassingInActionStatement
{
    static void Main(string[] args)
    {
        var dsufac = new DoSomethingUsefulForAChange();

        dsufac.Do(WriteToConsole);
        dsufac.Do2(s => WriteToConsoleWithSomethingExtra("Test"));
        dsufac.Do(WriteToConsoleWithSomethingExtra("Test")); // Does not compile
    }

    internal static void WriteToConsole()
    {
        Console.WriteLine("Done");
    }

    internal static void WriteToConsoleWithSomethingExtra(String input)
    {
        Console.WriteLine(input);
    }
}

internal class DoSomethingUsefulForAChange
{
    internal void Do(Action action)
    {
        action();
    }

    internal void Do2(Action<String> action)
    {
        action("");
    }
}

Первые 2 вызова работают, но мне интересно, почему третий не делает этого. Мне не нравится код внутри Do2, так как кажется странным, что у меня есть тип типа action(""), чтобы заставить его работать.

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

  • Почему я не могу написать третью строку с вызовом Do
  • Почему мне нужно написать action (""), чтобы заставить его работать в Do2

Ответы

Ответ 1

dsufac.Do(WriteToConsoleWithSomethingExtra("Test"));

фактически вызывает функцию сначала (WriteToConsoleWithSomethingExtra("Test")), а затем пытается передать результат в Do. Поскольку результата нет (void), это невозможно.

Что вы на самом деле хотите, так это:

dsufac.Do(() => WriteToConsoleWithSomethingExtra("Test"));

Внутренняя часть объявляет функцию, которая ничего не принимает (бит () =>), который вызывает WriteToConsoleWithSomethingExtra("Test") при выполнении. Тогда ваш вызов dsufac.Do получит действие, как ожидается.

Что касается Do2 - вы объявили его как принятие Action<String>, что означает, что action - это функция, которая принимает один аргумент. Вы должны передать ему строку. Эта строка может быть пустой, например, в вашем примере action(""), или она может быть передана извне, как в следующем:

dsufac.Do3(WriteToConsole, "Test");

...

internal void Do3(Action<String> action, String str)
{
    action(str);
}

Ответ 2

В вашем коде

dsufac.Do(WriteToConsoleWithSomethingExtra("Test"));

интерпретируется следующим образом

var variable = WriteToConsoleWithSomethingExtra("Test");
dsufac.Do(variable);

Как возвращаемый тип WriteToConsoleWithSomethingExtra ( "Тест" ) недействителен, поэтому вы не можете передать его на dsufac.Do(). Вот почему его не собирают. Но для первого

dsufac.Do(WriteToConsole);

вы не вызываете функцию, скорее вы передаете ее как группу методов, которая позже будет вызвана в методе Do() объекта dsufac. Но если вы хотите написать 3-ю строку как 1-го, вы можете использовать

dsufac.Do(() => WriteToConsoleWithSomethingExtra("Test"));

Ответ 3

  • Ожидает Action (т.е. метод, который не принимает никаких параметров и не возвращает значения). Следовательно, WriteToConsoleWithSomethingExtra не является допустимым положением - принимает один строковый параметр.
  • Do2 принимает Action<T> (т.е. метод, который принимает один параметр T и не возвращает значения). Следовательно, когда вы вызываете делегата/действие, вам нужно указать один параметр типа T, здесь String.

Ответ 4

Я делал что-то подобное в приложении Silverlight. Теперь, когда он бежит, часть его ломается.
Это происходит в дочернем окне Silverlight:

(my object context).SaveChanges(() => ChangesSaved());

private void ChagngesSaved()
{
   DialogResult = true: // is supposed to close the child window. line gets hit, does not close
}