为什么我的C#编译器(Visual Studio)不允许我使用try块执行此操作?

时间:2015-10-21 15:17:36

标签: c# visual-studio-2013

我在开发过程中有很多场景,我想做一些事情,比如

try 
{ 
    long presult = EvalInner(eqtn,new Tuple<int, int>(++begidx,curidx-1),eqtnArgs); 
}
catch ( Exception e ) 
{ 
    throw e; 
}
result = evalNewResult(result,lastop,presult,ref negateNextNum,ref negateNextOp); 
// ...
return presult;

然后我的编译器标记了行上的presult

result = evalNewResult(result,lastop,presult,ref negateNextNum,ref negateNextOp); 

  

这个名字&#39; presult&#39;在当前上下文中不存在

如果它很聪明,它会理解presult块中已初始化try,或者在使用presult之前退出该过程。

可能的解决方法(其中没有一个好):

  1. long presult;声明之前声明try。这使得编译器很生气,因为它错误地认为有可能返回一个未初始化的变量。
  2. 使用long presult = default(long)初始化它。这是有效的,但这是不好的做法,因为阅读代码的人不知道将其初始化为默认值是否可以解决1中描述的问题,或者因为值presult因为设置为默认值long在程序的上下文中有一些实际意义。
  3. 使用long? presult = null初始化它。这在语义上是更好的,因为它清楚地表明它意味着“在这一点上意味着没有价值”#34;而在2. 2.读者必须弄清楚推定具有无意义的价值。这里的问题是,null不仅需要额外的内存,而且我必须更改函数EvalInner以返回long?,这会产生一个链需要将更多long更改为long? s,我的程序最终会被null ified变量所淹没;这是一堆问号哈哈。
  4. 无论如何,我该如何处理这样的案件?

6 个答案:

答案 0 :(得分:2)

我将逐一回顾你的观点:

  
      
  1. 宣告长期的侮辱;就在try语句之前。这使得   编译器疯了,因为它错误地认为有可能   返回一个未初始化的变量。
  2.   

实际上,编译器正确确定有可能返回未初始化的变量。由于仅在右侧的函数成功时才设置变量,并且由于您在try..catch块中有它,因此函数可能会抛出而不返回,因此不会初始化变量。编译器不够聪明的是你正在捕获顶级异常并抛出(以一种糟糕的方式丢失堆栈跟踪)并且应该没有达到返回。但是有一些方法可以解决这个问题(主要是在调试期间通过拖动执行光标)。

  
      
  1. 使用long presult = default(long)初始化它。这有效,但是   这是不好的做法,因为阅读代码的人不知道   是否初始化它是默认值是解决   在1中描述的问题,或者是因为因为设定值而导致的价值   默认长期在上下文中有一些实际意义   程序
  2.   

由于longintshort等值类型必须具有值,因此这不是不好的做法。如果要将它们表示为没有值,请使用这些类型的可空版本(即long? presult = null)。

  
      
  1. 用多长时间初始化它? presult = null。这在语义上更好   因为很明显它意味着“寓意是没有价值的   在这一点上“而在2.”读者必须弄明白   presult具有无意义的价值。这里的问题不仅仅是   是否需要额外的内存来取消值,但我必须这样做   更改函数EvalInner返回一个长?这导致了   需要改变更多多头的连锁店和我的   程序最终散布着无效的变量;这是一个完整的   一团糟的问号哈哈。
  2.   

同样,函数必须返回一个有效long的值,所以如果你想返回一些容易被识别为错误值的东西,那么返回可空版本,否则你必须返回一个有效值。只有floatdoubleNaN个成员...

另一个选项是某种TryXXX方法,其中返回值是布尔值,您使用out long作为参数来存储结果。

答案 1 :(得分:0)

怎么样:

try 
{ 
    long presult = EvalInner(eqtn,new Tuple<int, int>(++begidx,curidx-1),eqtnArgs); 
    result = evalNewResult(result,lastop,presult,ref negateNextNum,ref negateNextOp); 
    // ...
    return presult;
}
catch ( Exception e ) 
{ 
    //Do some useful logging
    throw; //Don't lose stacktrace!
}

答案 2 :(得分:0)

我不明白你的问题。当您调用presult时,编译器无法知道evalNewResult的值,这就是您需要在try块之外声明它的原因。它是C#和许多其他语言范围的一般规则。

解决方案是在try块之前声明并初始化它。问题是“如果发生异常,应该有什么价值”。编译器不能问​​他这个问题。

答案 3 :(得分:0)

请检查此link以获得更多启发

  

编译器处于生成代码的业务中,该代码管理由该程序操纵的数据的存储。有许多不同的方法来生成代码来管理内存,但随着时间的推移,两种基本技术已经变得根深蒂固。

     

首先是要有某种长寿&#34;存储区域&#34;生命周期&#34;存储器中每个字节的长度 - 也就是说,它与某个程序变量有效关联的时间段 - 不能提前预测。编译器生成对#34;堆管理器的调用&#34;知道如何在需要时动态分配存储,并在不再需要时回收它。

     
    

第二种是短暂的生活&#34;存储区域,存储区中每个字节的生命周期是众所周知的,特别是存储区的生命周期遵循&#34;嵌套&#34;图案。也就是说,短寿命变量的寿命最长的分配与其后的短寿命变量的分配严格重叠。

         

局部变量遵循后一种模式;输入方法时,其局部变量变为活动状态。当该方法调用另一个方法时,新方法的局部变量就会生效。在第一种方法的局部变量死亡之前,他们已经死了。与局部变量相关的存储寿命的开始和结束的相对顺序可以提前计算出来。

  
     

出于这个原因,局部变量通常作为存储在&#34;堆栈上生成。数据结构,因为堆栈具有推送它的第一件事就是弹出的最后一件事。

因此整体局部变量是短暂的并且通常存储在堆栈中,为什么堆栈的效率高于其他变量。 Link了解更多信息为什么要叠加

答案 4 :(得分:0)

  

为什么我的C#编译器(Visual Studio)不允许我使用try块执行此操作?

这是因为大括号定义范围。来自Variable and Method Scope in Microsoft .NET

  

如果在块构造(如If语句)中声明变量,则该变量的作用域仅在块结束之前。生命周期一直持续到程序结束。

     

我应该如何处理这样的案件?

选择选项1.

  

这会让编译器生气,因为它错误地认为有可能返回未初始化的变量

选项1不会使编译器生气。编译器总是正确的: - )

我创建了以下SSCCE,它绝对有效:

using System;

namespace app1
{
    class Program
    {
        static void Main(string[] args)
        {
            Presult();
        }

        private static long Presult()
        {
            long presult;
            try
            {
                object eqtn = null;
                char begidx = '\0';
                int curidx = 0;
                object eqtnArgs = null;
                presult = EvalInner(eqtn, new Tuple<int, int>(++begidx, curidx - 1), eqtnArgs);
            }
            catch (Exception e)
            {
                throw e;
            }
            int result = 0;
            object lastop = null;
            object negateNextNum = null;
            object negateNextOp = null;
            result = evalNewResult(result, lastop, presult, ref negateNextNum, ref negateNextOp);
            // ...
            return presult;
        }

        private static int evalNewResult(int result, object lastop, long presult, ref object negateNextNum, ref object negateNextOp)
        {
            return 0;
        }

        private static long EvalInner(object eqtn, Tuple<int, int> tuple, object eqtnArgs)
        {
            return 0;
        }
    }
}

答案 5 :(得分:0)

  

我应该如何处理这样的案件?

正确的方法是选项1 。这并不能使编译器疯狂#34;因为事实上声明一个变量并在以后初始化它是允许从一开始就构造(不仅仅是针对这个特殊情况),并且编译器应该能够处理它正确没有问题。

IMO,唯一的缺点(或更好的说不便)是关键字ALTER TABLE tableName MODIFY 'first_history_column' AFTER column_name; ALTER TABLE tableName MODIFY 'second_history_column' AFTER 'first_history_column'; etc... 无法使用,因此必须明确指定类型。但有些反对使用var的人会说这确实是件好事:-)