简体   繁体   English

使用statement和try-catch() - 最后重复?

[英]Using statement and try-catch()-finally repetition?

The using(...) statement is syntactic sugar for try{} finally {}. using(...)语句是try {} finally {}的语法糖。

But if I then have a using statement like below: 但是,如果我有一个如下所示的使用声明:

using (FileStream fs = File.Open(path))
{


}

Now I want to catch the exceptions that opening this file could cause (and this is fairly high risk code in that it can fail due to the environment), but if I write try-catch inside would that not be repetition? 现在我想要捕获打开此文件可能导致的异常(这是相当高风险的代码,因为它可能因环境而失败),但是如果我在里面写try-catch会不会重复? When the code gets compiled to IL, I assume the repetition will get deleted when the code is JITted? 当代码被编译为IL时,我假设当代码被JIT时,重复将被删除?

However, I would want to catch the exceptions opening a file can cause (so I should wrap the try-catch outside the using statement's scope), and also the exceptions for whatever I do inside the using block so I should add the try-catch inside the block. 但是,我想要捕获打开文件可能导致的异常(所以我应该将try-catch包装在using语句的作用域之外),以及我在using块中做的任何异常,所以我应该添加try-catch在街区内。

This seems like I am adding a lot of repetition to what the CLR probably does inside. 这似乎是我在CLR可能在里面做了很多重复。 Does the CLR add catch clauses? CLR是否添加了catch子句?

My colleague argued that a using statement is messy (but this was because a single line was slightly long due to me hard coding them as I needed to change them very quickly and didn't have access to other parts of the code base). 我的同事认为使用声明是混乱的(但这是因为由于我需要对它们进行硬编码,所以单行很长,因为我需要非常快速地更改它们并且无法访问代码库的其他部分)。 Said colleague doesn't use the using statement but is there ever any functional difference between the using statement and try-finally/try-catch-finally? 说同事不使用using语句,但是using语句和try-finally / try-catch-finally之间是否存在任何功能差异? I did see one case of this where WCF services have a not well known corner case about using finally and returning values (something about finally). 我确实看到了一个这样的案例,其中WCF服务有一个关于使用finally和返回值的一个鲜为人知的角落案例(最后的事情)。 The solution was to use a check block. 解决方案是使用检查块。 Is there anything like this in C#? 在C#中有这样的东西吗?

On another note, are all types which implement IDisposale owners of unmanaged resources? 另一方面,是否所有类型都实现了非托管资源的IDisposale所有者? A discussion with my friend pointed the answer to being no. 与我的朋友的讨论指出了不是的答案。 (I have also read some threads in the using section of this forum, some very good knowledge there). (我也在本论坛的使用部分阅读了一些主题,其中有一些非常好的知识)。

My preference would be to keep the using statement and wrap it in a try/catch. 我的偏好是保留using语句并将其包装在try / catch中。 The outer try/catch will catch whatever exceptions you need to watch for, while ignoring the Dispose(). 外部try / catch将捕获您需要注意的任何异常,同时忽略Dispose()。 This has the added advantage of protecting you from yourself should you refactor this later to move the try/catch elsewhere (like in a calling function). 如果你稍后重构它以将try / catch移动到其他地方(比如在调用函数中),这还有一个额外的好处就是保护你自己。

As far as your question about IDisposable: Anyone can implement this for any reason they like. 关于IDisposable的问题:任何人都可以出于任何理由实现这一点。 There is no technical reason for it to be limited to unmanaged resources. 没有技术理由将其限制为非托管资源。 (Whether or not it should be limited to unmanaged resources in your code is a different question). (不论它是否应该你的代码被限制在非托管资源是一个不同的问题)。

You can implement the pattern yourself if you need to handle some exceptions if you really want to. 如果你真的想要处理一些异常,你可以自己实现这个模式。 Personally, I still find it simpler (and more importantly, clearer) to just wrap the using in try/catch block. 就个人而言,我仍然觉得在try / catch块中包装使用更简单(更重要的是,更清楚)。

But if you do it yourself, make sure you get it right. 但如果你自己做,请确保你做对了。 The Using block also creates an anonymous scope block so that your variable becomes eligible for collection sooner. Using块还会创建一个匿名范围块,以便您的变量更快地符合收集条件。 The .Dispose() method that's called at the end of the Using block only cleans up unmanaged resources, and so any memory your object holds may hang around a little longer. Using块末尾调用的.Dispose()方法只清理非托管资源,因此对象所保留的任何内存可能会挂起一段时间。 It's not likely a huge concern, but it is worth remembering just in case. 这不太可能是一个巨大的问题,但值得记住以防万一。

So, to do a direct adaption of the pattern, your code needs to look more like this: 因此,要直接调整模式,您的代码需要看起来更像这样:

{
    FileStream fs;
    try
    {
        fs = File.Open(path);

    }
    catch (FileNotFoundException e) { /* ... */ }
    catch (IOException e) { /* ... */ }
    catch (Exception e) {/* ... */}
    finally
    {
        if (fs != null) fs.Dispose();
    }
}

Personally, I'd like to see Using expanded to support Catch and Finally blocks. 就个人而言,我希望看到Using扩展来支持CatchFinally块。 Since they're already performing a transformation on the code, it doesn't seem like this would add that much additional complexity. 由于他们已经对代码进行了转换,因此这似乎不会增加额外的复杂性。

If you need to explicitly handle different exceptional cases which may happen during the statement, you could replace using with try/catch/finally and call Dispose() explicitly in the finally. 如果需要显式处理语句中可能发生的异常情况,可以using try/catch/finally替换,并在finally中显式调用Dispose() Or you could put a try/catch around the using block to separate exceptional circumstances from ensuring disposal. 或者您可以在using区块周围进行try/catch ,以将特殊情况与确保处置分开。

The only thing using does is ensure Dispose() is called even if an exception is thrown inside the block. 唯一 using是确保调用Dispose(),即使在块内抛出异常也是如此。 That is a very limited, highly specific implementation of the general try/[catch]/finally structure. 这是一般try/[catch]/finally结构的非常有限的,高度具体的实现。

The important thing is that none of these options have any real impact - as long as it does what you need, is readable and understandable, who cares? 重要的是,这些选项都没有任何实际影响 - 只要它能满足您的需求,可读性和可理解性,谁在乎? It's not like an extra try will be a bottleneck or anything! 这不是一个额外的尝试将是瓶颈或任何东西!

To answer your last question - no, IDisposable definitely does not necessarily mean the implementer has handles to unmanaged resources. 回答你的上一个问题 - 不,IDisposable绝对不一定意味着实现者可以处理非托管资源。 It's a far simpler and more generic pattern than that. 这是一个比这更简单,更通用的模式。 Here's one useful example: 这是一个有用的例子:

public class Timer : IDisposable
{
    internal Stopwatch _stopwatch;
    public Timer()
    {
        this._stopwatch = new Stopwatch();
        this._stopwatch.Start();
    }

    public void Dispose()
    {
        this._stopwatch.Stop();
    }
}

We can use this to time things without having to explicitly rely on start and stop being called by using: 我们可以使用它来计时,而不必通过使用显式依赖于启动和停止调用:

using(Timer timer = new Timer())
{
    //do stuff
}

The biggest difference between using and try-finally is that using will only call the Dispose() method. using和try-finally之间的最大区别是using只会调用Dispose()方法。 If you implement your own finally block can do other logic, for example logging, which would not be included in the using. 如果你实现自己的finally块可以做其他逻辑,例如日志记录,这将不包括在使用中。

The using construct is shorthand for a try/finally block. using构造是try/finally块的简写。 That is, the compiler generates: 也就是说,编译器生成:

FileStream fs = null;
try
{
    fs = File.Open(path);
    // ...
}
finally
{
    if (fs != null)
        fs.Dispose();
}

So it is proper to use using if you do not need a catch , but if you do then you should just use a normal try/catch/finally . 因此,如果您不需要catch ,则使用using是正确的,但如果您这样做,那么您应该使用正常的try/catch/finally

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM