Yield 返回与 try catch,如何解决?
我有一段代码:
using (StreamReader stream = new StreamReader(file.OpenRead(), Encoding))
{
char[] buffer = new char[chunksize];
while (stream.Peek() >= 0)
{
int readCount = stream.Read(buffer, 0, chunksize);
yield return new string(buffer, 0, readCount);
}
}
现在我必须用 try-catch 块包围它,
try
{
using (StreamReader stream = new StreamReader(file.OpenRead(), Encoding))
{
char[] buffer = new char[chunksize];
while (stream.Peek() >= 0)
{
int readCount = stream.Read(buffer, 0, chunksize);
yield return new string(buffer, 0, readCount);
}
}
}
catch (Exception ex)
{
throw ExceptionMapper.Map(ex, file.FullName)
}
我看不到任何方法来做我想做的事情。
该方法具有签名:
public IEnumerable<string> ReadPieces(int pieces)
我需要一个 try
/catch
并在 catch
情况下调用 ExceptionMapper
。 该方法由所有调用者延迟使用。
我必须捕获的异常来自这些调用:
File.OpenRead()
stream.Read()
I've a piece of code:
using (StreamReader stream = new StreamReader(file.OpenRead(), Encoding))
{
char[] buffer = new char[chunksize];
while (stream.Peek() >= 0)
{
int readCount = stream.Read(buffer, 0, chunksize);
yield return new string(buffer, 0, readCount);
}
}
Now I have to surround this with an try-catch block
try
{
using (StreamReader stream = new StreamReader(file.OpenRead(), Encoding))
{
char[] buffer = new char[chunksize];
while (stream.Peek() >= 0)
{
int readCount = stream.Read(buffer, 0, chunksize);
yield return new string(buffer, 0, readCount);
}
}
}
catch (Exception ex)
{
throw ExceptionMapper.Map(ex, file.FullName)
}
I can't see any way to do what I want.
The method has the signature:
public IEnumerable<string> ReadPieces(int pieces)
I need a try
/catch
with a call to the ExceptionMapper
in the catch
case.
The method is used deferred by all callers.
The exceptions I have to catch are coming from these calls:
File.OpenRead()
stream.Read()
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
这是一个适合我的代码片段(我没有达到错误条件)。
Here is a code snippet, which works for me (I did not reach the error condition).
因为您希望在枚举期间保持 Stream 打开并处理异常并正确关闭文件句柄,所以我认为您不能使用常规枚举快捷方式(迭代器块、yield-return/yield-休息)。
相反,只需执行编译器会为您执行的操作并添加一些:
通过自己实现 IEnumerator,您还可以添加 IDisposable
我没有对此进行测试,但我认为它已经很接近了。
像这样使用:
编辑:我完全忘记添加 try-catch 块放置。哎呀。
Because you want to keep the Stream open for the duration of the enumeration AND deal with exceptions AND properly close the file handle either way, I don't think you can use a regular enumeration shortcut (the iterator block, yield-return/yield-break).
Instead, just do what the compiler would have done for you and add some:
By implementing IEnumerator yourself, you can also add IDisposable
I didn't test this, but I think it's close.
used like this:
EDIT: I had totally forgotten to add the try-catch block placements. Oops.
您不能在 try/catch 块中使用
yield
构造。将 try 块限制为可以抛出异常的代码,而不是全部。如果你做不到这一点,那你就运气不好了——你需要把它放在堆栈的更上方。You can't use
yield
constructs in a try/catch block. Restrict the try block to code that can throw, not all of it. If you are unable to do this, you are out of luck - you'll need to catch it further up the stack.编辑 - 这个答案实际上是不正确的,由于评论中详细阐述的原因 - “仅包装了枚举器生成,而不是迭代本身。” - 但我将这个答案留在这里作为举个例子,有时看似有效的方法由于语言的复杂性而并非如此。
把它当作一个警示故事——我感谢 uosɐſ。 =)
这是一个选项 - 将您的方法分为两种方法,一种是公共方法,一种是私有方法。公共方法是对私有方法(即生成器)的调用的包装器(带有 try/catch)。例如:
这将允许您的用户依赖具有内置异常处理功能的生成器。此外,您可以对公共方法中的输入执行更多验证,根据需要抛出由于错误输入而导致的任何异常,并在调用方法时立即执行这些验证,而不是等待第一次枚举可枚举项。
Edit - this answer is actually incorrect, due to the reasons elaborated on in the comments - "ONLY the enumerator generation is wrapped, but not the iteration itself." - but I am leaving this answer here as an example of how sometimes what may appear to work does not due to the intricacies of the language.
Consider it a cautionary tale - my thanks to uosɐſ. =)
Here's an option - separate your method into two methods, one public and one private. The public method is a wrapper (with try/catch) around a call to the private method, which is your generator. For example:
This will allow your users to rely on the generator having built-in exception handling. Additionally you could perform more validation on your inputs in the public method, throwing any exceptions as needed due to bad inputs, and have those validations performed immediately when the method is called, rather than waiting for the first time the enumerable is enumerated.
看看这个问题。在特殊情况下,您可以在
try/catch
子句之后使用yield break
,yield value
。我担心性能,但据信try
不会对性能产生影响,同时不会引发异常。Take a look at this question. You can
yield break
in the exceptional case,yield value
after thetry/catch
clause. I was concerned about performance, but there it is believed thattry
doesn't have a performance influence while no exceptions are thrown.您可以考虑使用 System.Interactive 包的高级 LINQ 功能,特别是
Catch
和抛出
运算符。只需将现有代码移动到 local function,并对本地函数的结果应用Catch
运算符:Catch
运算符的签名:Throw 的签名
运算符:上面的代码将无法编译,因为
file
变量不存在于handler
委托的上下文中。如果必须以延迟方式构建文件
,您可以使用Defer
运算符:如果您想抑制异常(而不是将其映射到不同的例外),请执行以下操作:
如果您的序列是异步的(
IAsyncEnumerable
),您可以找到相同的Catch
,抛出
和
Defer
System.Interactive.Async 包。You could consider using the advanced LINQ functionality of the System.Interactive package, and specifically the
Catch
andThrow
operators. Just move your existing code in a local function, and apply theCatch
operator on the result of the local function:The signature of the
Catch
operator:The signature of the
Throw
operator:The above code will not compile, because the
file
variable does not exist in the context of thehandler
delegate. In case thefile
must be constructed in a deferred manner, you can use theDefer
operator:In case you want to suppress the exception (instead of mapping it to a different exception), do this:
In case your sequence is asynchronous (
IAsyncEnumerable<string>
), you can find identicalCatch
,Throw
andDefer
operators for asynchronous sequences in the System.Interactive.Async package.不幸的是,您还没有描述您想要做什么,但您可以尝试强制您定义的函数的用户尝试/捕获自己:
Unfortunately you haven't described what it is you want to do, but you could try just forcing users of the function you're defining to try/catch themselves:
一种有效的策略(如果读起来有点混乱……)是分解并包装可能会绕过实际
yield return
调用的每个部分。这可以解决该问题,以便yield
本身不在 try/catch 块中,但仍包含可能失败的部分。这是您的代码的可能实现:
One strategy is that effective (if a bit messier to read...) is to break out and wrap each section that might throw around the actual
yield return
call. This works around the issue so that theyield
itself is not in a try/catch block, but the parts that could fail are still contained.Here's a possible implementation of your code:
尝试在枚举器方法中使用本地函数:将 try..catch 的内容移动到本地函数,然后从 try..catch 中调用该函数。
使用您的示例:
在很多情况下,使用本地函数实际上是一个好主意。使用此模式可以强制该方法立即验证参数,而不是等到调用者开始枚举(Roslyn 警告 RCS1227)。
Try using a local function within the enumerator method: move the contents of the try..catch to the local function, then call the function from within the try..catch.
Using your example:
Using a local function is actually a good idea in a lot of cases. Using this pattern can force the method to validate arguments immediately instead of waiting until the caller begins enumeration (Roslyn warning RCS1227).
尝试这种方法:
您只需要小心使用这种方法:您必须根据严重性过滤异常。有些异常必须停止整个过程,而其他异常则可以跳过并记录。
try this approach :
You just have to be careful with this approach: you will have to filter exceptions based on the severity. Some exceptions will have to stop the whole process, others just can be skipped and logged.
另一个考虑因素 - 如果您使用实现内部引发异常的
IEnumerable
方法实现yield
,则无法捕获该单个错误并继续枚举 - 请参阅“ https://msdn.microsoft.com/en-us/ 的“异常处理”部分Library/9k7k7cf0.aspx示例:
结果为
请注意,枚举器的
Current
在“常规枚举”期间“卡在”最后一次成功的MoveNext
上,而处理的异常允许它完成循环。Another consideration -- if you're consuming an
IEnumerable
method implementingyield
that internally throws an exception, you can't catch that individual error and continue enumerating -- see the "Exception Handling" section of https://msdn.microsoft.com/en-us/library/9k7k7cf0.aspxexample:
results in
Note that the enumerator's
Current
is "stuck" on the last successfulMoveNext
during "regular enumeration", whereas the handled exceptions allows it to complete the loop.