Return income using try catch, how can I solve it

I have a 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 need to surround this with a 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 see no way to do what I want.

EDIT Method has a signature

 public IEnumerable<string> ReadPieces(int pieces) 

I need try catch with calling ExceptionMapper in case of catch . This method is used by deferred by all callers.

The exceptions that I have to catch come from these calls

 File.OpenRead() stream.Read() 
+14
c # try-catch yield-return
Feb 21 2018-11-21
source share
9 answers

Since you want Stream to be open at the time of enumeration AND handle exceptions and close the file descriptor correctly anyway, I don’t think you can use a regular enumeration shortcut (iterator block, yield-return / exit-pause).

Instead, just do what the compiler would do for you, and add the following:

By implementing IEnumerator yourself, you can also add IDisposable

 public class LazyStream : IEnumerable<string>, IDisposable { LazyEnumerator le; public LazyStream(FileInfo file, Encoding encoding) { le = new LazyEnumerator(file, encoding); } #region IEnumerable<string> Members public IEnumerator<string> GetEnumerator() { return le; } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return le; } #endregion #region IDisposable Members private bool disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { if (le != null) le.Dispose(); } disposed = true; } } #endregion class LazyEnumerator : IEnumerator<string>, IDisposable { StreamReader streamReader; const int chunksize = 1024; char[] buffer = new char[chunksize]; string current; public LazyEnumerator(FileInfo file, Encoding encoding) { try { streamReader = new StreamReader(file.OpenRead(), encoding); } catch { // Catch some generator related exception } } #region IEnumerator<string> Members public string Current { get { return current; } } #endregion #region IEnumerator Members object System.Collections.IEnumerator.Current { get { return current; } } public bool MoveNext() { try { if (streamReader.Peek() >= 0) { int readCount = streamReader.Read(buffer, 0, chunksize); current = new string(buffer, 0, readCount); return true; } else { return false; } } catch { // Trap some iteration error } } public void Reset() { throw new NotSupportedException(); } #endregion #region IDisposable Members private bool disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { if (streamReader != null) streamReader.Dispose(); } disposed = true; } } #endregion } } 

I have not tested this, but I think it is close.

used as follows:

 using (var fe = new LazyStream(new FileInfo("c:\\data.log"), Encoding.ASCII)) { foreach (var chunk in fe) { Console.WriteLine(chunk); } } 

EDIT: I completely forgot to add try-catch block placements. Unfortunately.

+13
Feb 21 '11 at 15:08
source share
β€” -

Here is a snippet of code that works for me (I have not reached the error condition).

 while (true) { T ret = null; try { if (!enumerator.MoveNext()) { break; } ret = enumerator.Current; } catch (Exception ex) { // handle the exception and end the iteration // probably you want it to re-throw it break; } // the yield statement is outside the try catch block yield return ret; } 
+21
Aug 21 '12 at 17:44
source share

Edit - this answer is actually incorrect due to the reasons discussed in the comments: "ONLY counter generation is wrapped, but not the iteration itself." - but I leave this answer here as an example of how sometimes what may seem to work is not related to the subtleties of the language.

Consider this a warning story - thanks, thanks. =)




Here's the option - separate your method into two methods: one open and one closed. A public method is a wrapper (with try / catch) around invoking a private method that is your generator. For example:

 public IEnumerable<string> YourFunction(...) { try { return _yourFunction(...); } catch (Exception e) { throw ExceptionMapper.Map(e, file.FullName); } } private IEnumerable<string> _yourFunction(...) { // Your code here } 

This allows your users to rely on a generator that has built-in exception handling. In addition, you can perform a large check on your inputs in a public method, throwing any exceptions as necessary due to bad inputs and performing these checks immediately when the method is called, rather than waiting for the enumerated first time to be enumerated.

+8
Feb 21 2018-11-21T00:
source share

You cannot use yield constructs in a try / catch block. Limit the try block to code that can be thrown, not all. If you cannot do this, you are out of luck - you need to catch it further up the stack.

+7
Feb 21 2018-11-21T00:
source share

Take a look at this question . You can yield break in the exception yield value after a try/catch clause. I was concerned about performance, but it is believed that try does not affect performance, while exceptions are not thrown.

+5
Feb 21 2018-11-21T00:
source share

Unfortunately, you didn’t specify what exactly you want to do, but you can try just to force users of the function you define to try / catch yourself:

 public IEnumerable<string> YourFunction(...) { //Your code } //later: //... try{ foreach( string s in YourFunction(file) ) { //Do Work } } catch(Exception e){ throw ExceptionMapper.Map(e, file.FullName); } 
+1
Feb 21 '11 at 14:40
source share

try this approach:

 public IEnumerable<ReturnData> toto() { using (StreamReader stream = new StreamReader(File.OpenRead(""), Encoding.UTF8)) { char[] buffer = new char[1]; while (stream.Peek() >= 0) { ReturnData result; try { int readCount = stream.Read(buffer, 0, 1); result = new ReturnData(new string(buffer, 0, readCount)); } catch (Exception exc) { result = new ReturnData(exc); } yield return result; } } } public class ReturnData { public string Data { get; private set; } public Exception Error { get; private set; } public bool HasError { get { return Error != null; } } public ReturnData(string data) { this.Data = data; } public ReturnData(Exception exc) { this.Error = exc; } } 

You just need to be careful with this approach: you will have to filter exceptions based on severity. Some exceptions should stop the whole process; others may simply be skipped and logged.

0
Feb 21 '11 at 14:50
source share

Another consideration: if you use the IEnumerable method that implements yield , which internally throws an exception, you cannot catch this separate error and continue the enumeration - see the section "Exception Handling" at https://msdn.microsoft.com/en-us/ library / 9k7k7cf0.aspx

Example:

 void Main() { // even is okay, odd will cause exception var operations = new[] { 2, 16, 5 /* ! */, 8, 91 /* ! */ }; var results = process(operations); var en = results.GetEnumerator(); "Regular Enumeration".Title(); testEnumeration(en); results = process(operations, ex => log("Handled: {0}", ex.Message)); en = results.GetEnumerator(); "Handled Exceptions".Title(); testEnumeration(en); results = process(operations, ex => log("Handled+: {0}", ex.Message), true); en = results.GetEnumerator(); "Handled Exceptions and Continue".Title(); testEnumeration(en); } /// run the test and debug results void testEnumeration(IEnumerator en) { int successCount = 0, failCount = 0; bool keepGoing = false; do { try { log("==={0}===", "before next"); keepGoing = en.MoveNext(); log("==={0}=== (keepGoing={1}, curr={2})", "after next", keepGoing, en.Current); // did we have anything? if(keepGoing) { var curr = en.Current; log("==={0}===", "after curr"); log("We did it? {0}", curr); successCount++; } } catch(InvalidOperationException iopex) { log(iopex.Message); failCount++; } } while(keepGoing); log("Successes={0}, Fails={1}", successCount, failCount); } /// enumerable that will stop completely on errors IEnumerable<int> process(IEnumerable<int> stuff) { foreach(var thing in stuff) { if(thing % 2 == 1) { throw new InvalidOperationException("Aww, you broked it"); } yield return thing; } } /// enumerable that can yield from exceptions IEnumerable<int> process(IEnumerable<int> stuff, Action<Exception> handleException, bool yieldOnExceptions = false) { bool shouldYield = false; foreach(var thing in stuff) { var result = thing; try { if(thing % 2 == 1) { throw new InvalidOperationException("Aww, you broked it"); } shouldYield = true; } catch(Exception ex) { handleException(ex); // `yield break` to stop loop shouldYield = yieldOnExceptions; if(yieldOnExceptions) result = -1; // so it returns a different result you could interpret differently } if(shouldYield) yield return result; } } void log(string message, params object[] tokens) { Console.WriteLine(message, tokens); } 

leads to

  Regular Enumeration --------------------------- ===before next=== ===after next=== (keepGoing=True, curr=2) ===after curr=== We did it? 2 ===before next=== ===after next=== (keepGoing=True, curr=16) ===after curr=== We did it? 16 ===before next=== Aww, you broked it ===before next=== ===after next=== (keepGoing=False, curr=16) Successes=2, Fails=1 Handled Exceptions -------------------------- ===before next=== ===after next=== (keepGoing=True, curr=2) ===after curr=== We did it? 2 ===before next=== ===after next=== (keepGoing=True, curr=16) ===after curr=== We did it? 16 ===before next=== Handled: Aww, you broked it ===after next=== (keepGoing=True, curr=8) ===after curr=== We did it? 8 ===before next=== Handled: Aww, you broked it ===after next=== (keepGoing=False, curr=8) Successes=3, Fails=0 Handled Exceptions and Continue --------------------------------------- ===before next=== ===after next=== (keepGoing=True, curr=2) ===after curr=== We did it? 2 ===before next=== ===after next=== (keepGoing=True, curr=16) ===after curr=== We did it? 16 ===before next=== Handled+: Aww, you broked it ===after next=== (keepGoing=True, curr=-1) ===after curr=== We did it? -1 ===before next=== ===after next=== (keepGoing=True, curr=8) ===after curr=== We did it? 8 ===before next=== Handled+: Aww, you broked it ===after next=== (keepGoing=True, curr=-1) ===after curr=== We did it? -1 ===before next=== ===after next=== (keepGoing=False, curr=-1) Successes=5, Fails=0 

Note that the Current enumerator is stuck in the last successful MoveNext during the regular enumeration, while the processed exceptions allow it to complete the loop.

0
Sep 18 '15 at 15:57
source share

One strategy is that effective (if you read the messier bit ...) is to tear and wrap each section that might cause the actual call to yield return . This works around the problem, so the yield itself is not in the try / catch block, but the parts that may fail are still contained.

Your code implementation is possible here:

 StreamReader stream = null; char[] buffer = new char[chunksize]; try { try { stream = new StreamReader(file.OpenRead(), Encoding); } catch (Exception ex) { throw ExceptionMapper.Map(ex, file.FullName); } int readCount; Func<bool> peek = () => { try { return stream.Peek() >= 0; } catch (Exception ex) { throw ExceptionMapper.Map(ex, file.FullName); } }; while (peek()) { try { readCount = stream.Read(buffer, 0, chunksize); } catch (Exception ex) { throw ExceptionMapper.Map(ex, file.FullName); } yield return new string(buffer, 0, readCount); } } finally { if (stream != null) { stream.Dispose(); stream = null; } } 
0
Sep 18 '17 at 15:34 on
source share



All Articles