Тип аргумента '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
}