【问题标题】:return statement before finallyfinally 之前的 return 语句
【发布时间】:2013-05-05 22:54:17
【问题描述】:

我有一个作为客户端的 c# 程序,许多客户端程序又是一个 c# windows 应用程序连接到这个 c# 服务器程序以从 sqlite 数据库中读取数据。为了避免连接多个客户端时出现锁定问题,我使用了下面的代码,

System.Threading.Monitor.Enter(Lock);

try                       
{                     
    filter.Execute();//get data from database
    Request.Clear();
    return filter.XML;//create xml and return to client
}
finally
{
    System.Threading.Monitor.Exit(Lock);
}

服务器有时会挂起,需要重新启动服务器程序。 在finally之前做return语句是不是一个好习惯?

问候 桑吉塔

【问题讨论】:

  • 这只是编写 lock 语句的困难方法。否则,确保只有一个进程可以使用 SqlLite 是没有用的,您需要使用命名的 Mutex。

标签: c# return-value


【解决方案1】:

来自MSDN

通过使用 finally 块,您可以清理在 try 块中分配的任何资源,并且即使在 try 块中发生异常,您也可以运行代码。通常,finally 块的语句在控制离开 try 语句时运行。正常执行、break、continue、goto 或 return 语句的执行,或者从 try 语句中传播异常都可能导致控制权转移。

在处理的异常中,保证运行相关的 finally 块。但是,如果异常未处理,finally 块的执行取决于异常展开操作的触发方式。反过来,这取决于您的计算机的设置方式。有关详细信息,请参阅 CLR 中的未处理异常处理。

【讨论】:

    【解决方案2】:

    是的,这就是 finally 语句的用途。会在return之后执行,即使发生异常

    编辑: 这个简单的代码会告诉你,执行 finally 块不需要 catch 块

    public Form1()
    {
        InitializeComponent();
        check();
    }
    
    private string check()
    {
        try
        {
            return String.Empty;
        }
        finally
        {
            MessageBox.Show("finally");
        }
    }
    

    【讨论】:

    • @bash.d 这是定义的行为。你读过你发布的内容吗? :D "通常情况下,finally 块的语句在控制离开 try 语句时运行。控制的转移可以作为正常执行的结果发生......或返回语句"
    • 好吧...你永远不会停止学习,虽然这很奇怪。
    • @VladL。我认为 finally 不会按原样执行代码 - 没有使用 catch 子句进行异常处理。未处理的异常最终会改变一切。
    • @AndyBrown 为什么想得太久,创建简单的项目并测试我的编辑;)
    • @VladL。公平地说,我对“我不认为”的使用过于模糊:“我知道”。 C# language documentation 声明 finally 不会总是被调用(在异常未处理的情况下)。虽然您已经展示了它确实被调用的情况,但这并不表明它在其他情况下仍不能保持未调用状态。
    【解决方案3】:

    由于您没有 catch 块,因此无法保证 finally 将被执行。来自MSDN - try-finally (C# Reference)"Locks and exceptions do not mix" (Eric Lippert)

    在处理的异常中,保证关联的 finally 块 要运行。但是,如果异常未处理,则执行 finally 块取决于异常展开操作的方式 触发。反过来,这取决于您的计算机的设置方式。

    从随后提到的链接 (Unhandled Exception Processing In The CLR) 中,有各种考虑因素可能意味着您最终会终止线程。不过,老实说,我不知道这是否会让你在 lock 对象上留下一个锁。

    如果您想确保:

    • 你释放了锁;但是
    • 您不想在此级别处理异常,而是希望由更高级别的异常处理程序处理它

    然后做:

    TheXmlType xml = null;
    Monitor.Enter(Lock);
    bool inLock = true;
    try {
      ...
      xml = filter.Xml; // put this here in case it throws an exception
      inLock = false; // set this here in case monitor.exit is to 
        // throw an exception so we don't do the same all over again in the catch block
      Monitor.Exit(Lock);
      return xml; // this is fine here, but i would normally put it outside my try
    }
    catch (Exception) {
      if (inLock) Monitor.Exit(Lock);
      throw;
    }
    

    但是,请注意:不要使用catch (Exception) 隐藏异常,这只有在您重新抛出异常时才可以。人们还建议您使用单个 return 语句,这通常会在您的 try 块之外。

    编辑:

    通过测试程序确认,来自MSDN - Exceptions in Managed Threads

    从通用语言 .NET Framework 2.0 版开始 运行时允许线程中大多数未处理的异常继续进行 自然。在大多数情况下,这意味着未处理的异常 导致应用程序终止。

    因此,如果您不处理异常,您的应用程序将会崩溃(您不必担心锁定问题)。如果您确实处理了它,那么您的原始代码将在 finally 块中执行它,您就可以了。

    编辑 2: 测试代码已更新,因为它没有正确说明最终未触发:

    class Program
    { 
        static void Main(string[] args) {
            Program p =new Program();
            p.Start();
            Console.WriteLine("done, press enter to finish");
            Console.ReadLine();
        }
    
        private readonly object SyncRoot = new object();
        ManualResetEvent mre = new ManualResetEvent(false);
    
        private void Start() {
            /*
             * The application will run the thread, which throws an exception
             * While Windows kicks in to deal with it and terminate the app, we still get 
             * a couple of "Failed to lock" messages
             * */
    
            Thread t1 = new Thread(SetLockAndTerminate);
            t1.Start();
            mre.WaitOne();
            for (int i = 0; i < 10; i++) {
                if (!Monitor.TryEnter(this.SyncRoot, 1000)) {
                    Console.WriteLine("Failed to lock");
                }
                else {
                    Console.WriteLine("lock succeeded");
                    return;
                }
            }
            Console.WriteLine("FINALLY NOT CALLED");
        }
        public int CauseAnOverflow(int i)
        {
            return CauseAnOverflow(i + 1);
        }
        public void SetLockAndTerminate() {
            Monitor.Enter(this.SyncRoot);
            Console.WriteLine("Entered");
            try {
                mre.Set();
                CauseAnOverflow(1); // Cause a stack overflow, prevents finally firing
            }
            finally {
                Console.WriteLine("Exiting");
                Monitor.Exit(this.SyncRoot);
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      Is it bad practice to return from within a try catch finally block?
      这是在 c# 中编写异常处理的正确方法,并且总是会执行 finally 块,它不依赖于返回的位置。 我不知道您的代码,但您应该在其他地方找到您的问题(例如,如果您的代码托管在 IIS 中,我会怀疑 Lock 对象在不同加载域中的状态,或者锁可能只是针对一个来电而发生在数据库中或什么是 Request.Clear() 你那里没有锁块?)。您可以轻松记录来电状态并找出问题所在。

      【讨论】:

        猜你喜欢
        • 2013-06-04
        • 2015-10-01
        • 2011-08-07
        • 1970-01-01
        • 2018-03-21
        • 1970-01-01
        • 2020-06-17
        • 2012-06-25
        相关资源
        最近更新 更多