【问题标题】:Winform Recursive loop - Method is called again and again [closed]Winform递归循环-一次又一次调用方法[关闭]
【发布时间】:2018-02-06 19:53:15
【问题描述】:

请随意创建一个 Windows 窗体应用程序。要重现错误,请禁用网络连接并运行代码。它每 1 秒尝试重新连接一次。在 4-5 次尝试启用网络连接后,在调试模式下,您会注意到 Reconnect() 方法被调用了 4-5 次,即使产品已被获取。获取产品后为什么要一次又一次地调用 Reconnect() 方法?

        string apiUrl = "https://api.gdax.com/products";
        string json;

        private void Form1_Load(object sender, EventArgs e)
        {            
            try
            {                
                if (FillProducts()) // product need first
                {
                }
            }
            catch (WebException ex)
            {
                ReconnectOnError(ex);
            }
        }
        private bool FillProducts()
        {
            bool isDone = false;
            try
            {
                json = GetGDXJSONData(apiUrl);
                JsonSerializer serializer = new JsonSerializer();
                DataTable dt = (System.Data.DataTable)Newtonsoft.Json.JsonConvert.DeserializeObject(json, (typeof(System.Data.DataTable)));

                count = dt.Rows.Count;

                if (count > 0)
                    isDone = true;
            }
            catch (Exception ex)
            {               
                isDone = false;
                ReconnectOnError(ex);
            }
            return isDone;
        }

        int count = 0;
        private void ReconnectOnError(Exception errorMessage)
        {
            try
            {
                Thread.Sleep(1000);
                if (count < 1)
                {
                    FillProducts();     // it comes on this point again and again even the count is greater than 1
                    Reconnect();
                }
                else
                {
                    Reconnect();
                }
            }
            catch (WebException ex)
            {
                ReconnectOnError(ex);
            }
        }

        private void Reconnect()
        {
            // why this is called the number of times the attempt was made to fill the products?
        }      

        private string GetGDXJSONData(string apiUrl)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl);

            request.Method = "GET";
            request.ContentType = "application/json";
            request.UserAgent = "gdax-node-client";
            request.Accept = "application/json";

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();

            return responseString;
        }
    }

编辑 - 这是一个示例代码,一旦获取产品,我将在 Reconnect() 方法中做很多事情。 此外,如果在获取产品后发生错误,则不要获取产品,只需调用 Reconnect() 方法,这就是 else 的原因。

Edit2 - 请不要仅仅通过查看代码来回复。如果您创建了一个表单并自己运行它并且可以自己看到错误,那么请告知如何解决此问题。

更新 - 我知道我进入了无限迭代。我试过了,现在可以了:

    string apiUrl = "https://api.gdax.com/products";
    string json;

    private void Form1_Load(object sender, EventArgs e)
    {
        if (FillProducts()) // product need first
        {
        }
    }

    bool isOk = false;
    private string GetGDAXProducts()
    {
        try
        {
            json = GetGDXJSONData(apiUrl);
            return json;
        }
        catch (Exception ex)
        {               
            return "-1";
        }
    }

    int count = 0;
    private bool FillProducts()
    {
        bool isDone = false;
        string retVal = GetGDAXProducts();

        while (retVal == "-1")
        {
            retVal = GetGDAXProducts();
        }

        if (retVal != "-1")
        {
            JsonSerializer serializer = new JsonSerializer();
            DataTable dt = (System.Data.DataTable)Newtonsoft.Json.JsonConvert.DeserializeObject(json, (typeof(System.Data.DataTable)));

            count = dt.Rows.Count;

            if (count > 0)
                isDone = true;
        }

        return isDone;
    }

    private string GetGDXJSONData(string apiUrl)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl);

        request.Method = "GET";
        request.ContentType = "application/json";
        request.UserAgent = "gdax-node-client";
        request.Accept = "application/json";

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();

        return responseString;
    }

【问题讨论】:

  • 这是由于if (count &lt; 0)中的其他部分
  • 你在fillproducts之后再调用它...
  • 我必须这样做。但它并没有出现在那条线上。如果你运行代码,你会看到它调用了 FillProducts();重新连接();一次又一次,即在没有互联网时第一次尝试重新连接的次数
  • 你有一个间接递归调用——FillProducts()调用ReconnectOnError(ex);如果有一个异常调用FillProducts()
  • 请在没有互联网的情况下运行代码并调试它,然后在尝试重新连接 4-5 次后启动互联网。请不要停止应用程序

标签: c# winforms


【解决方案1】:

你这里有一个无意的循环:

如果FillProducts失败,它会调用自己...

  1. 迭代:FP 失败
  2. 迭代:FP 调用 ReconnectOnEx 调用 FP,再次失败
  3. 迭代:FP 调用 ReconnectOnEx 调用 FP 调用 ReconnectOnEx ...

n.迭代:....调用成功并返回的FP。

现在整个堆栈将随着每次迭代调用 Reconnect 展开。

private bool FillProducts()
    {
        bool isDone = false;
        try
        {
            /* ... Fails if no connection ... */
        }
        catch (Exception ex)
        {               
            isDone = false;
            ReconnectOnError(ex); // ==> BLOCKS !!
        }
        return isDone;
    }

    int count = 0;
    private void ReconnectOnError(Exception errorMessage)
    {
        try
        {
            Thread.Sleep(1000);
            if (count < 1)
            {
                FillProducts();     // <== Will result in another call to this method. Returns on 1st Succeeding call to FillProducts.
                Reconnect();        // <== Will be called as soon as FillProducts returns.
            }
            else
            {
                Reconnect();
            }
        }
        catch (WebException ex)
        {
            ReconnectOnError(ex);
        }
    }

为避免这种情况,您可以将“重试”逻辑移到 FillProducts 方法中:

private bool FillProducts()
    {
        // To prevent waiting forever ...
        int retryCount = 10;

        bool isDone = false;
        while ( !isDone && (retryCount-- > 0) )
        {
            try
            {
                /* ... Fails if no connection ... */

                // OnSuccess=>
                isDone = true; // will break the loop.
            }
            catch (Exception ex) // You should actually catch a more specific Exception here
                                 // ... but that's a different question.
            {               
                isDone = false;
                // Just omit this! >>> ReconnectOnError(ex); // ==> BLOCKS !!
                // If you want, you can add a little delay here ...
                Thread.Sleep(1000);
                // From your code, I guess this has to be called on failure ...
                Reconnect();
            }
        }
        return isDone;
    }

需要考虑的其他几点:

  1. 您不应该在 GUI 线程上执行网络 I/O。您的 GUI 可能会变得无响应。考虑使用 async/await(任务异步模式)

  2. 捕捉Exception 可能不是最好的主意。您应该尽可能捕捉到最具体的异常,其余的则由调用者处理。

【讨论】:

    【解决方案2】:

    当没有互联网连接时。计数变量总是为零。

    【讨论】:

    • 请点击“在此输入图片描述”链接
    【解决方案3】:

    这是因为如果没有网络连接,那么调用 API url 将无法连接,并且在您的 catch 块中您正在尝试重新连接

            catch (Exception ex)
            {               
                isDone = false;
                ReconnectOnError(ex);
            }
    

    【讨论】:

    • 是的,我正在尝试重新连接,但我的问题是一旦互联网恢复,为什么它仍然调用 4-5 次 Reconnect() 方法?
    猜你喜欢
    • 2022-08-18
    • 1970-01-01
    • 2021-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-28
    • 2016-05-26
    • 2016-03-14
    相关资源
    最近更新 更多