none
【C#】一些惯用写法的疑惑? RRS feed

  • 问题

  • 各位前辈好,我C#入门已有一段时间,一些常见情况下形成了一些惯用写法,一直想知道这些写法符不符合规范?性能又如何?有什么误区需要走出来?等等……苦于还未到研究IL的水平,所以先来请教一下,还望不吝赐教:

    1、循环时,通常都有一个终止测试量,这个量很可能是Array.Length,DataTable.Rows.Count之类的需要访问某集合/数组的属性,所以我的做法是

    int total = Array.Length;//先用变量存储测试量

    for (int i=0; i < total; i++)//再让计数器与变量对比,这样我感觉不必每圈都访问Array.Length,性能会更好(当然循环体执行的语句不能改变Array.Length,否则当然不能这样)

    {...}

    请问:这样有必要吗?其实直接写成i < Array.Length会不会也不影响性能

    2、在【提交】或【确定】按钮的处理函数中,我会首先检查各个条件的合法性,惯用法是如果有不合法的条件就抛异常,并把该异常显示给用户,请看代码:

    private void btnDown_Click(object sender, EventArgs e)

    {

    try

    {

    if (condition1 == xxx){throw new Exception("条件1不能为xxx");}

    if (condition2 == xxx){throw new Exception("条件2不能为xxx");}

    ...

    DoSomething;//如果未抛过异常,然后才开始干正事

    }

    catch (Exception ex)

    {

    MessageBox.Show(ex.Message);

    }

    }

    请问:在这种用于条件判断的应用场景中,使用主动抛异常的方式合适吗?如果不合适,请问有什么更好的惯用法?

    3、与第2条有点类似,也是条件判断的。某些事件触发后,总是希望满足特定条件再进行处理,例如DataGridView的CellClick,如果只希望单击内容才处理,需要这样:

     private void dgvInfo_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
     {
          if (e.RowIndex != -1 && e.ColumnIndex != -1)

                {DoSomething;……}

    }

    这样当主条件与分支条件较多的时候,整个方法体的顶部和底部会充满了花括号,正式代码也被退格了若干级,不利于代码阅读,所以后来我改为这样:

    private void dgvInfo_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
    {
          if (e.RowIndex == -1 || e.ColumnIndex == -1) { return; }//若不符合处理条件,直接退出事件处理函数

          DoSomething;

    }

    这个惯用法的确与上面的第2条比较像,但我的理由仅仅是不想嵌套过多的if else和出现太多的花括号,请问这样做好吗?

    2012年6月9日 3:13

答案

  • 问题1:Length是一个属性,本身就是返回一个int类型的数值。因此不会影响。

    问题2、3:这是习惯问题,不要太纠结了。如果满足了某个条件return表示跳出了该函数,后面就不会再判断了。如果你的if很多,而恰恰只有最后一个才满足,此时return照例反而要高于if的性能。

    谢谢!


       QQ我:讨论(Talk)
    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处

    2012年6月9日 5:17
    版主
  • Array.Length里边存放了一个变量,该变量响应每次对List自身增加或者减少设置的。绝对不是像你想象的每次调用再循环!

    我用反编译器看了ArrayList:

    public int Length
    {
        get
        {
            return this.m_length;
        }
        set
        {
            if (value < 0)
            {
                throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            }
            int arrayLength = GetArrayLength(value, 0x20);
            if ((arrayLength > this.m_array.Length) || ((arrayLength + 0x100) < this.m_array.Length))
            {
                int[] destinationArray = new int[arrayLength];
                Array.Copy(this.m_array, destinationArray, (arrayLength > this.m_array.Length) ? this.m_array.Length : arrayLength);
                this.m_array = destinationArray;
            }
            if (value > this.m_length)
            {
                int index = GetArrayLength(this.m_length, 0x20) - 1;
                int num3 = this.m_length % 0x20;
                if (num3 > 0)
                {
                    this.m_array[index] &= (((int) 1) << num3) - 1;
                }
                Array.Clear(this.m_array, index + 1, (arrayLength - index) - 1);
            }
            this.m_length = value;
            this._version++;
        }
    }
     

       QQ我:讨论(Talk)
    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处

    • 已标记为答案 ahdung_AI 2012年6月9日 12:58
    2012年6月9日 11:48
    版主
  • 1、无需这样做,微软的C#编译器会自动进行优化,也就是说编译以后的结果和你的示例代码是一致的,直接使用属性节省了你的时间。

    2、不要让用户直接看异常,首先,异常是给开发人员看的。其次异常有可能暴露系统内部的结构,引发安全问题。最后,引发一个异常的开销是很大的,这是没必要的负担。

    3、正确做法,大大提高的代码的可读性。


    快乐在于能够长时间的为自己认为值得的事情努力工作,不管它是什么。

    2012年6月9日 11:51
  • 对于第2种情况,我想了想改成这样:

    string msg = string.Empty;

    if (condition1 == xxx){msg = "条件1不能为xxx";}

    if (condition2 == xxx){msg = "条件2不能为xxx";}

    ...

    if (msg.Length != 0)//只要msg不为空字串,说明有条件不合法

    {

       MessageBox.Show(msg);

       Return;//显示完消息就退出方法

    }

    //若未被return再干正事

    //DoSomething...

    这样就不必引发异常造成大量开销。

    2012年6月11日 2:25

全部回复

  • >>请问:这样有必要吗?其实直接写成i < Array.Length会不会也不影响性能

    是的,直接写Array.Length即可。

    >>请问:在这种用于条件判断的应用场景中,使用主动抛异常的方式合适吗?如果不合适,请问有什么更好的惯用法?

    可以,不过抛异常给用户感觉交互不是很好,我习惯直接用if判断:

    private void btnDown_Click(object sender, EventArgs e)
    {
    if (condition1 == xxx){MessageBox.Show(……);}
    else if (condition2 == xxx){MessageBox.Show("条件2不能为xxx");}
    ...
    else
    {
    DoSomething;//如果未抛过异常,然后才开始干正事
    }
    }

    >>这个惯用法的确与上面的第2条比较像,但我的理由仅仅是不想嵌套过多的if else和出现太多的花括号,请问这样做好吗?

    完全可以!


       QQ我:讨论(Talk)
    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处

    2012年6月9日 3:43
    版主
  • 感谢回复,还想讨论:

    1、每圈访问Array.Length真的不会影响性能吗?难道编译成IL时就会对这种情况做优化处理?还是JIT优化?

    2、的确抛异常的性能我想比不上if

    3、还是纠结性能,return我想还是比不上if

    2012年6月9日 5:10
  • 问题1:Length是一个属性,本身就是返回一个int类型的数值。因此不会影响。

    问题2、3:这是习惯问题,不要太纠结了。如果满足了某个条件return表示跳出了该函数,后面就不会再判断了。如果你的if很多,而恰恰只有最后一个才满足,此时return照例反而要高于if的性能。

    谢谢!


       QQ我:讨论(Talk)
    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处

    2012年6月9日 5:17
    版主
  • 属性也是函数,调用肯定就有个执行过程,如果编译器或JIT没优化处理,访问属性怎么都比不上访问变量,而循环起来每圈都要访问,感觉很疼。如果知道Array.Length的get访问器是如何编写的就能弄清楚了,搞不好内部实现也是个循环
    2012年6月9日 11:35
  • Array.Length里边存放了一个变量,该变量响应每次对List自身增加或者减少设置的。绝对不是像你想象的每次调用再循环!

    我用反编译器看了ArrayList:

    public int Length
    {
        get
        {
            return this.m_length;
        }
        set
        {
            if (value < 0)
            {
                throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            }
            int arrayLength = GetArrayLength(value, 0x20);
            if ((arrayLength > this.m_array.Length) || ((arrayLength + 0x100) < this.m_array.Length))
            {
                int[] destinationArray = new int[arrayLength];
                Array.Copy(this.m_array, destinationArray, (arrayLength > this.m_array.Length) ? this.m_array.Length : arrayLength);
                this.m_array = destinationArray;
            }
            if (value > this.m_length)
            {
                int index = GetArrayLength(this.m_length, 0x20) - 1;
                int num3 = this.m_length % 0x20;
                if (num3 > 0)
                {
                    this.m_array[index] &= (((int) 1) << num3) - 1;
                }
                Array.Clear(this.m_array, index + 1, (arrayLength - index) - 1);
            }
            this.m_length = value;
            this._version++;
        }
    }
     

       QQ我:讨论(Talk)
    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处

    • 已标记为答案 ahdung_AI 2012年6月9日 12:58
    2012年6月9日 11:48
    版主
  • 1、无需这样做,微软的C#编译器会自动进行优化,也就是说编译以后的结果和你的示例代码是一致的,直接使用属性节省了你的时间。

    2、不要让用户直接看异常,首先,异常是给开发人员看的。其次异常有可能暴露系统内部的结构,引发安全问题。最后,引发一个异常的开销是很大的,这是没必要的负担。

    3、正确做法,大大提高的代码的可读性。


    快乐在于能够长时间的为自己认为值得的事情努力工作,不管它是什么。

    2012年6月9日 11:51
  • 非常感谢!你们的答复对我很有帮助,谢谢!
    2012年6月9日 12:59
  • 非常感谢!你们的答复对我很有帮助,谢谢!
    不用谢!欢迎下次再来,互帮互助!

       QQ我:讨论(Talk)
    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处

    2012年6月9日 23:56
    版主
  • 对于第2种情况,我想了想改成这样:

    string msg = string.Empty;

    if (condition1 == xxx){msg = "条件1不能为xxx";}

    if (condition2 == xxx){msg = "条件2不能为xxx";}

    ...

    if (msg.Length != 0)//只要msg不为空字串,说明有条件不合法

    {

       MessageBox.Show(msg);

       Return;//显示完消息就退出方法

    }

    //若未被return再干正事

    //DoSomething...

    这样就不必引发异常造成大量开销。

    2012年6月11日 2:25