usar try practices handling español ejemplo como catch best c# .net-3.5 try-catch yield-return

practices - try catch sql exception c#



rendimiento de retorno con try catch, ¿cómo puedo resolverlo (9)

Tengo un código:

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); } }

Ahora tengo que rodear esto con un bloque 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) }

No puedo ver ninguna manera de hacer lo que quiero.

EDITAR El método tiene la firma.

public IEnumerable<string> ReadPieces(int pieces)

Necesito una try catch con una llamada al ExceptionMapper en el caso de catch . El método es utilizado diferido por todos los que llaman.

Las excepciones que tengo que atrapar provienen de estas llamadas.

File.OpenRead() stream.Read()


Aquí hay un fragmento de código, que funciona para mí (no alcancé la condición de error).

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; }


Debido a que desea mantener el flujo abierto durante la enumeración Y lidiar con las excepciones Y cerrar adecuadamente el identificador del archivo, no creo que pueda usar un acceso directo de enumeración regular (el bloque de iterador, rendimiento-retorno / rendimiento- descanso).

En su lugar, simplemente haga lo que el compilador hubiera hecho por usted y agregue algunos:

Al implementar IEnumerator usted mismo, también puede agregar 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 } }

No probé esto, pero creo que está cerca.

utilizado de esta manera:

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

EDITAR: Me había olvidado totalmente de agregar las ubicaciones de bloque try-catch. Ups.


Desafortunadamente, no ha descrito qué es lo que quiere hacer, pero podría intentar forzar a los usuarios de la función que está definiendo para que los intente / atrape a sí mismos:

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); }


Echa un vistazo a esta pregunta . Puede yield break en el caso excepcional, yield value después de la cláusula try/catch . Me preocupaba el rendimiento, pero se cree que el try no tiene una influencia en el rendimiento, mientras que no se lanzan excepciones.


Editar: esta respuesta es realmente incorrecta, debido a las razones detalladas en los comentarios: "SOLO la generación del enumerador está envuelta, pero no la iteración en sí". - Pero estoy dejando esta respuesta aquí como un ejemplo de cómo a veces lo que puede parecer que funciona no se debe a las complejidades del lenguaje.

Considérelo un cuento de advertencia, gracias a UOS. =)

Aquí hay una opción: separe su método en dos métodos, uno público y otro privado. El método público es un contenedor (con try / catch) alrededor de una llamada al método privado, que es su generador. Por ejemplo:

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

Esto permitirá que sus usuarios confíen en que el generador tenga incorporado el manejo de excepciones. Además, podría realizar más validación en sus entradas en el método público, lanzando las excepciones que sean necesarias debido a entradas incorrectas, y realizar esas validaciones inmediatamente cuando se llama al método, en lugar de esperar la primera vez que se enumera el enumerable.


No se pueden usar construcciones de yield en un bloque try / catch. Restrinja el bloque try al código que puede lanzar, no todo. Si no puedes hacer esto, estás fuera de suerte, tendrás que ir más lejos en la pila.


Otra consideración: si está consumiendo un método de implementación IEnumerable que yield una excepción internamente, no puede detectar ese error individual y continuar la enumeración, consulte la sección "Manejo de excepciones" de https://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx

ejemplo:

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); }

resultados en

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

Tenga en cuenta que la Current del enumerador está "atascada" en el último MoveNext exitoso durante la "enumeración regular", mientras que las excepciones manejadas le permiten completar el ciclo.


Una estrategia es que eficaz (si es un poco más difícil de leer ...) es romper y envolver cada sección que pueda yield return llamada de yield return real. Esto soluciona el problema, por lo que el yield sí no está en un bloque try / catch, pero las partes que podrían fallar todavía están contenidas.

Aquí hay una posible implementación de su código:

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; } }


intente este enfoque:

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; } }

Solo debe tener cuidado con este enfoque: deberá filtrar las excepciones según la gravedad. Algunas excepciones tendrán que detener todo el proceso, otras solo se pueden omitir y registrar.