如何重构这段代码?

4

我正在使用Visual C#设计一个简单的计算器,但是我遇到了一个令人烦恼的运行时错误(这对于静态类型语言来说很有趣)。

首先让我向您展示部分代码:

private float get_input()
{
    try
    {
        return float.Parse(textBox1.Text);
    }
    catch
    {
        textBox2.Clear();
        textBox2.AppendText("Invalid Input");
        return 0;
    }

}

private void button1_Click(object sender, EventArgs e)
{ 
    textBox2.Clear();
    float v = get_input();
    textBox2.AppendText((Math.Sin(v)).ToString());
}

问题在于当我运行程序,并且输入框里输入了 "a" 这个字符时,我的程序会通过在输出框中显示 "Invalid input" 来处理异常。然而,它接着会计算默认浮点类型的 sincos 等等。因此,输出框中的答案看起来像是:"invalid input1" 或 "invalid input0"。我提供了一张截图:

我确实意识到这是因为在调用 get_input() 方法后紧接着告诉它追加文本,但我真的不知道如何解决这个问题。我已经试过以 7 种方式重新构建我的代码,但总是有些问题。

你真的不知道如何让get_input方法表明它失败了,以便调用者可以决定不运行计算吗?我至少能想到三种方法。 - Euphoric
使用 float.TryParse,如果成功则返回 true,否则返回 false(无需抛出异常),然后仅在尝试解析返回 true 时执行计算。 - Tim
你可以让你的方法返回一个 string,然后在你的 click 事件中解析/处理它。 - Drew Kennedy
6个回答

9
请使用TryParse而不是try...catch
private void button1_Click(object sender, EventArgs e)
{ 
    textBox2.Clear();
    float result;
    if (float.TryParse(textBox1.Text, out result))
    {
         textBox2.AppendText(Math.Sin(result).ToString());
    }
    else
    {
        textBox2.Text = "Invalid Input";
    }
}

2
这是我认为最好的方法。 - Tim

2
这是因为您无法告诉调用代码发生了错误。您可以采用两种方式来解决这个问题。
  1. Trap the exception as now but change the return type to a nullable float (float?) and set it to null in the error case:

    private float? get_input()
    {
        try
        {
            return float.Parse(textBox1.Text);
        }
        catch
        {
            textBox2.Clear();
            textBox2.AppendText("Invalid Input");
            return null;
        }
    }
    

    Then you have to trap that condition:

    private void button1_Click(object sender, EventArgs e)
    { 
        textBox2.Clear();
        float? v = get_input();
        if (v != null) textBox2.AppendText((Math.Sin(v)).ToString());
    }
    
  2. Let the exception propagate up to your button click code. In this case your get_input method becomes a one liner and can be removed:

    private void button1_Click(object sender, EventArgs e)
    { 
        textBox2.Clear();
        try
        {
            float v = float.Parse(textBox1.Text);
            textBox2.AppendText((Math.Sin(v)).ToString());
        }
        catch
        {
            textBox2.Clear();
            textBox2.AppendText("Invalid Input");
        }
    }
    
两种方法都可以,但第二种更简洁。

非常感谢!!!我曾经考虑过通过设置一个布尔值来告诉我的代码发生了错误,例如 bool errorFound = false; 但是当我不得不在 true 和 false 之间切换时,它会导致复杂的问题,因为一旦它变成 false,所有其他计算都会失败。 - katie1245

1

这对你有用吗?在这样一个简单的场景中,我不认为需要单独的函数。

private void button1_Click(object sender, EventArgs e)
{ 
    textBox2.Clear();
    try
    {
        float v = float.Parse(textBox1.Text);
        textBox2.AppendText(Math.Sin(v).ToString());
    }
    catch
    {
        textBox2.AppendText("Invalid Input");
    }
}

如果您想要一个单独的函数,可以这样做:
private float? try_get_input()
{
    try
    {
        return float.Parse(textBox1.Text);
    }
    catch
    {
        return null;
    }
}

private void button1_Click(object sender, EventArgs e)
{ 
    textBox2.Clear();
    float? v = try_get_input();
    if (v != null)
    {
        textBox2.AppendText(Math.Sin(v.Value).ToString());
    }
    else
    {
        textBox2.AppendText("Invalid Input");
    }
}

1
像这样的东西,也许?
private bool try_get_input(out float val)
{
    val = 0;

    try
    {
        val = float.Parse(textBox1.Text);
        return true;
    }
    catch
    {
        textBox2.Clear();
        textBox2.AppendText("Invalid Input");
    }

    return false;
}

private void button1_Click(object sender, EventArgs e)
{ 
    textBox2.Clear();

    float v = 0;

    if (try_get_input(out v))
    {
        textBox2.AppendText((Math.Sin(v)).ToString());
    }
}

1
为什么要重复造轮子呢?直接使用 float.TryParse() 就好了。 - Tim
好的,没问题。我试图在他的代码范围内保持一致。 - 0x1mason

1

如果在解析函数get_input中抛出异常,将返回0的值。然而,调用函数button1_Click没有办法知道异常是否被抛出。


0
问题在于您在get_input中并没有真正表明出现了错误。我提出两种解决方案来解决这个问题:
  1. 处理 NaNNaN 代表“非数字”。例如,如果计算 2.0 的反正弦值,则通常会使用它。通常情况下,NaN 会通过每个表达式传播。然后在您的 button1_Click 方法中检测到它:

    private float get_input() {
        try {
            return float.Parse(textBox1.Text);
        }
        catch {
            textBox2.Clear();
            textBox2.AppendText("无效输入");
            return float.NaN;
        }
    }
    
    private void button1_Click(object sender, EventArgs e) { 
        textBox2.Clear();
        float v = get_input();
        if(v != float.NaN) {
            textBox2.AppendText((Math.Sin(v)).ToString());
        }
    }
    
  2. 我认为的方法是使用一个高阶方法:一种为其提供委托的方法:

    private void calculateResult (Func<float,float> f) {
        textBox2.Clear();
        try {
            float v = float.Parse(textBox1.Text);
             textBox2.AppendText((f(v)).ToString());
        }
        catch {
            textBox2.AppendText("无效输入");
            return float.NaN;
        }
    }
    

    现在,您可以通过使用lambda表达式简单地提供对 Math.Sin 的引用来调用该方法:

    private void button1_Click(object sender, EventArgs e) { 
        calculateResult(x => (float) Math.Sin(x));
    }
    

    此外,这是一种优雅的解决方案,因为您可以轻松枚举要实现的所有函数:

    private void button1_Click(object sender, EventArgs e) { 
        calculateResult(x => (float) Math.Sin(x));
    }
    
    private void button2_Click(object sender, EventArgs e) { 
        calculateResult(x => (float) Math.Cos(x));
    }
    
    private void button3_Click(object sender, EventArgs e) { 
        calculateResult(x => (float) Math.Tan(x));
    }
    

    因此,在这种方法中,您可以重用代码。这使得代码更易于调试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接