【问题标题】:Not sure if I'm using "using" correctly c# in tftp app不确定我是否在 tftp 应用程序中正确使用“使用”c#
【发布时间】:2014-07-31 23:14:01
【问题描述】:

我正在尝试将 this 预制的 C# tftp 服务器应用程序与我的 windows c# 表单一起使用。在作者的服务器示例中,效果很好,他使用了一个控制台应用程序。当我尝试将他的控制台示例移植到我的表单应用程序中时,它不起作用(没有错误,只是无法连接),我相信我的问题出在“使用”语句中:

using (var server = new TftpServer())
{
    server.OnReadRequest += new TftpServerEventHandler(server_OnReadRequest);
    server.OnWriteRequest += new TftpServerEventHandler(server_OnWriteRequest);
    server.Start();
    Console.Read();
}

不确定我是否理解正确,但我相信 Console.Read() 会阻止应用退出。如果是这种情况,我将如何使用表单应用程序实现等价物。我只是无法理解“使用”。抱歉,我是 C# 新手。

【问题讨论】:

    标签: c# winforms tftp


    【解决方案1】:

    Windows 窗体将始终保持打开状态,直到用户明确关闭它们。它们总是有一个线程读取消息队列以获取用户输入,因此它们不会像无限制的控制台应用程序那样退出。在 Windows 窗体中,我们必须比在控制台应用程序中更多地担心多线程和并发性。它大多是自然产生的,但并非总是如此。

    因此,您不能真正使用 Console.Read() 的等效项来推迟 using 处置的执行,直到用户请求它。如果你这样做了,你的表单只会显得没有响应。

    不过,你很幸运! C# 中的using 块只不过是在处理完对象后记住调用IDisposable.Dispose() 的语法糖。因此,在 Forms 项目中与此等价的可能只是将 server 对象存储在类范围的字段中,然后在 Button.Click 事件上调用 server.Dispose()。当然,这只是一个例子。如果觉得更合适,您也可以在 Form.Closing 上进行操作。

    高层,你想做这样的事情:

    1. 在表单类中声明一个字段TftpServer server;
    2. 注册一个Load 事件以及您需要的任何内容,以便您的server 在您的构造函数中运行。
    3. Form_Load 事件中打开您的server 字段。
    4. Form 的生命周期中使用您认为合适的server 事件。您可能不必担心并发问题,但这是另一个问题的问题。
    5. 在表单的Dispose 事件中调用server.Dispose()

    本质上,

    class main : Form
    {
        private TftpServer server;
    
        public main()
        {
            InitializeComponent();
    
            this.Load += main_Load;
    
            server = new TftpServer();
            server.OnReadRequest += new TftpServerEventHandler(server_OnReadRequest);
            server.OnWriteRequest += new TftpServerEventHandler(server_OnWriteRequest);
        }
    
        private void main_Load(object sender, EventArgs e)
        {
            server.Start();
        }
    
        private void server_OnReadRequest(/* I wasn't sure of the arguments here */)
        {
            // use the read request: give or fetch its data (depending on who defines "read")
        }
        private void server_OnWriteRequest(/* I wasn't sure of the arguments here */)
        {
            // use the write request: give or fetch its data (depending on who defines "write")
        }
    
        protected override void Dispose(bool disposing)
        {
            if (server != null) // since Dispose can be called multiple times
            {
                server.Dispose();
                server = null;
            }
        }
    }
    

    【讨论】:

    • 您甚至可能不需要显式调用 Dispose,只要表单对象超出范围,让垃圾收集器为您完成工作即可。
    • @Neolisk 是的,很好,你可以让 GC 处理它。但我倾向于更明确。它只是减少了任何错误的风险。很难知道 GC 什么时候会出现,所以我会说,在这种情况下,如果有未处理的异常或类似的东西,它应该被视为一种备用策略。
    • 覆盖表单的 Dispose 而不是使用 Closing 事件可能会更好。另外@Neolisk您应该手动调用Dispose,您不知道服务器类是否有一个终结器来调用Dispose,即使有,这也不是IDisposable的预期用途。
    • @AssortedTrailmix 这是非常真实的,所有这一切。我完全忘记了覆盖Dispose。自从我使用 Windows 窗体以来已经太久了。我更新的语法是正确的,对吧?我目前不在 WinForms 编译器附近对其进行测试,但我认为它是正确的。我只是想确保我没有在上面犯一些愚蠢的错误。
    • 唯一需要注意的是非空对象可以被 Disposed,你唯一能做的就是捕获 ObjectDisposedException。
    【解决方案2】:

    问题是处置服务器是关闭它的原因。请记住,使用只是语法糖。以下两个代码块 [实际上] 等效:

    var foo = new Foo();
    try
    {
       foo.Do();
    }
    finally
    {
       foo.Dispose();
    }
    

    using (var foo = new Foo())
    {
       foo.Do();
    }
    

    您可以阻止主线程在控制台应用程序中退出,但在表单应用程序中它是不同的。问题不在于您需要通过执行某种阻塞操作将线程保持在 using 中。那会很糟糕,并且该行为会锁定您的表单应用程序。问题是你不想使用 using。您希望在启动服务器时对其进行更新,然后在应用程序退出或停止单击时,使用 Dispose() 显式处理它。

    【讨论】:

      【解决方案3】:

      在控制台应用程序中,您的 TftpServer 实例一直在监听,直到线程退出,这仅在按下由 Console.Read() 检测到的键之后发生

      在您的表单应用程序中,Console.Read() 没有等待,因此 using 块完成,这会导致您的服务器实例超出范围。

      因此,您并没有完全滥用using,而是预期用途对您没有帮助。看看使用task parallel library 让一些后台任务异步运行。

      【讨论】:

        【解决方案4】:

        一个也可以作为答案的小注释,你可以在这里使用一个 using 块,你只需将它放在你的 main 函数中:

        ...(make your form and stuff) 
        using (var server = new TftpServer())
        {
           server.OnReadRequest += new TftpServerEventHandler(server_OnReadRequest);
           server.OnWriteRequest += new TftpServerEventHandler(server_OnWriteRequest);
           server.Start();
           Application.Run(yourFormHere); //This blocks until the form is closed
        }
        

        我忘记提及的另一个选项是覆盖表单中的 Dispose。你可能想这样做。使用此选项,您可以保证您的服务器将被处置(禁止某些会阻止它以任何方式处置的事件[即内存不足])

        【讨论】:

          猜你喜欢
          • 2011-02-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-03
          • 2010-10-17
          • 2010-10-28
          相关资源
          最近更新 更多