为什么需要 Lambda 函数将值传递给 @onclick 调用的方法?

Fli*_*tTD 2 html c# blazor blazor-webassembly

我是 Blazor(以及一般的 Web 开发人员)的新手。我正在关注Microsoft 的 Blazor Web 应用程序待办事项列表教程,在完成该教程后,我想进一步在每个列表元素旁边添加按钮以将它们从列表中删除。这是我为实现这一目标而编写的代码:

@page "/todo"

<h1>Todo (@todos.Count(todo => !todo.IsDone))</h1>

<ul>
    @foreach (var todo in todos)
    {
        <li>
            <input type="checkbox" @bind="todo.IsDone" />
            <input @bind="todo.Title" />
            <button @onclick="RemoveTodo(todo)">Remove</button>
        </li>
    }
</ul>

<input placeholder="Something to do" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

@code {
    private List<TodoItem> todos = new();
    private string newTodo;
    
    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
    
    private void RemoveTodo(TodoItem item)
    {
        if (item != null)
        {
            todos.Remove(item);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我以为我可以复制语法来<button @onclick="AddTodo">Add todo</button>添加此按钮,但这引入了一个错误。正如我通过此 Stack Overflow 答案发现的那样,为了修复此错误(并允许构建应用程序),我必须更改:

<button @onclick="RemoveTodo(todo)">Remove</button>

包含一个 lambda 函数,如下所示:

<button @onclick="() => RemoveTodo(todo)">Remove</button>

我知道这个更改是有效的,因为我测试了它并且应用程序的行为符合我的预期!但我想知道为什么这个改变会起作用。

我发现了这个附加的 Stack Overflow 问题,其中所选的答案解释了为了以上述方式将值传递给 @onclick 调用的方法,必须使用 lambda 表达式。答案说 using@onclick将导致编译器创建一个 EventCallback 对象来处理我提供给 的代码@onclick

但是,我仍然不明白为什么我的原始代码不起作用。我假设当将值传递给正在执行的函数时,由 EventCallback 对象生成的委托无法正确执行。第二个问题表明,通过 lambda 函数调用会产生一种不同类型的委托,它可以解析传递给函数的值。

我对正在发生的事情的理解是否接近事实?为什么我需要以这种方式将函数封装在 lambda 函数中,但仅当所述函数被传递值时才需要?

Hen*_*man 5

这是关于 C# 语法的。事件处理程序必须是委托(对方法的引用)。

显示错误的最短方法:

<button @onclick="AddTodo">Add todo</button>         // ok
<button @onclick="AddTodo()">Add todo</button>       // error
<button @onclick="() => AddTodo()">Add todo</button> // ok
Run Code Online (Sandbox Code Playgroud)

因为,用简单的 C# 来说:

delegate x = AddTodo;    // ok, a methodname w/o () means "address off"
delegate x = AddTodo();  // error, void is not a delegate
delegate x = () => AddTodo();  // ok, a lambda is a delegate
Run Code Online (Sandbox Code Playgroud)

我们通常更喜欢第一个较短的版本。但一旦你有争论,你就需要长形式。