我花了一些时间学习C#中的Delegates,并连接了一个包含两个表单的小型Windows表单应用程序来测试它们.

我的目标是让AddContactForm使用EventHandler代表将新的联系人数据推送回合同表单.我目前使用下面的代码来处理这个问题,但最终我希望将AddContactForm中的ContactAddedHandler设为私有,并将一个函数传递给AddContactForm构造函数,该构造函数用于订阅委托,如下所示:

public AddContactForm(SomeSubscriberFunction foo)
{
    ContactAddedHandler += foo;
}

我try 了SomeSubscriberFunction种不同的排列组合,但都没有成功:

  • Func<object?, AddContactEventArgs?>
  • Func<AddContactEventArgs?>
  • Action<object?, AddContactEventArgs?>

如果有人能就以下两个问题提供任何指导,将不胜感激:

#1.我是否以一种完全非最佳实践的方式处理这个问题?如果是这样,正确的方法是什么?

#2.如果这种使用委托的方法是正确的,我如何才能实现我试图做的事情?

谢谢.

合同表单

public void addContactBtn_Click(Object sender, EventArgs e)
{
    AddContactForm addContactForm = new AddContactForm();
    addContactForm.ContactAddedHandler += ContactAdded;
    addContactForm.Show();
}

public void ContactAdded(object? sender, AddContactForm.AddContactEventArgs e)
{
    Console.Write("Contact Added");
}

AddContactForm

 public partial class AddContactForm : Form
 {

     public class AddContactEventArgs(Contact contact) : EventArgs 
     {
         public Contact contact = contact;
     }

     public event EventHandler<AddContactEventArgs>? ContactAddedHandler;

     public AddContactForm()
     {
         InitializeComponent();
     }

     private void saveContactBtn_Click(object sender, EventArgs e)
     {
         Contact c = new Contact("Test");
         AddContactEventArgs args = new AddContactEventArgs(c);
         if (ContactAddedHandler != null)
         {
             ContactAddedHandler(this, args);
         }
     }
 }

推荐答案

在类似的场景中,操作委托经常用于生成通知.您已经有了一个Contact class对象,该对象存储所需的信息,因此您很可能不需要自定义EventArgs对象来包装相同的数据.

例如,如果操作委托通知仅限于创建窗体类的代码:

Note: written for .NET 6+, since you had the 100 tag

public partial class ContactForm : Form {
    public ContactForm() => InitializeComponent();

    private void AddContactBtn_Click(object sender, EventArgs e) {
        Action<Contact> contactAdded = (contact) => Debug.WriteLine(contact.Name);
        AddContactForm addContact = new(contactAdded);
        addContact.ShowDialog();
    }
}

如果Action委托只能通过Form的构造函数注入,如本文所示,您可以简单地将委托分配给私有Field并在需要时调用它:

public partial class AddContactForm : Form {
    Action<Contact>? NotifyNewContact = null;
    public AddContactForm() => InitializeComponent();
    public AddContactForm(Action<Contact> newContact) : this() => NotifyNewContact = newContact;

    private void SaveContactBtn_Click(object sender, EventArgs e) {
        NotifyNewContact?.Invoke(new("Test"));
    }
}

You could also make the Action accessible from the outside and add more than one subscriber.
You can use a multicast delegate as it was used before. But better create a level of indirection, adding the event keyword, so add() and remove() methods are created under the hood, to protect the object.

AddContactForm的构造函数中,您可以做到这一点:

public AddContactForm(Action<Contact> newContact) : this() => NotifyNewContact = newContact;
// Instead of NotifyNewContact += newContact

但不是这个,而是其他地方:

addContact.NotifyNewContact = (c) => Debug.WriteLine(c.Name);
// Instead of += (c) => [...];

public partial class AddContactForm : Form {
    protected internal event Action<Contact>? NotifyNewContact = null;
    public AddContactForm() => InitializeComponent();
    public AddContactForm(Action<Contact> newContact) : this() => NotifyNewContact += newContact;

    private void SaveContactBtn_Click(object sender, EventArgs e) {
        NotifyNewContact?.Invoke(new("Test"));
    }
}

然后,可以将多个代理(其中AddContactForm的实例可见)添加到可以传递Action代理的任何其他对象,或以许多不同的其他方式添加:

public partial class ContactForm : Form {
    // [...]

    private void AddContactBtn_Click(object sender, EventArgs e) {
        Action<Contact> contactAdded = (contact) => Debug.WriteLine(contact.Name);
        AddContactForm addContact = new(contactAdded);

        // Subscribe a second time, just a lambda
        addContact.NotifyNewContact += (c) => Debug.WriteLine(c.Name);

        // Subscribe using a delegate that something else provided
        // Assume otherContact is coming from elsewhere
        Action<Contact> otherContact = new((c)=> Debug.WriteLine(c.Name));
        addContact.NotifyNewContact += otherContact;

        // Subscribe using a method that matches the signature
        addContact.NotifyNewContact += ProcessContact;
       
        // [...]
    }

   // [...]
    private void ProcessContact(Contact contact) {
        Debug.WriteLine(contact.Name);
    }
}

Csharp相关问答推荐

Blazor在FluentButton onClick事件上设置参数

C#阻塞调用或await calling inside calling方法

为什么Regex.IsMatch(";\\t";,";\\t";)返回FALSE而不是TRUE?

在.NET 7.0 API控制器项目中通过继承和显式调用基类使用依赖项注入

Savagger使用Fastendpoint更改用户界面参数

HttpRequestMessage.SetPolicyExecutionContext不会将上下文传递给策略

Polly重试URL复制值

在implementationFactory中避免循环依赖

升级后发出SWITCH语句

具有类型识别的泛型方法

在C#ASP.NET内核中使用INT AS-1进行控制器场景的单元测试

MSI无法将快捷方式添加到启动文件夹

匿名类型的AbstractValidator

多个选项卡上的MudForm验证

将ValueTask发送给调用者

持久函数似乎没有并行运行活动函数

Visual Studio Publish生成的文件少于Build(.NET Framework)生成的文件

如何显示;CA1806:不要忽视方法结果;对于不可变列表<;T>;.添加

从目录中获取文件的异步函数未按预期工作

ASP.NET Core 6 Web API项目无法通过postman连接SignalR