使用try catch返回yield,我该如何解决它

Khh*_*Khh 21 c# try-catch yield-return .net-3.5

我有一段代码:

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);
    }
 }
Run Code Online (Sandbox Code Playgroud)

现在我必须使用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)
}
Run Code Online (Sandbox Code Playgroud)

我看不出有什么方法可以做我想做的事.

编辑 该方法具有签名

public IEnumerable<string> ReadPieces(int pieces)
Run Code Online (Sandbox Code Playgroud)

我需要一个try catch与该呼叫ExceptionMappercatch情况.所有呼叫者都推迟使用该方法.

我必须抓住的例外情况来自这些电话

File.OpenRead()
stream.Read()
Run Code Online (Sandbox Code Playgroud)

小智 31

这是一个代码片段,适用于我(我没有达到错误条件).

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;
}
Run Code Online (Sandbox Code Playgroud)


Jas*_*ban 14

因为您希望在枚举期间保持Stream打开并处理异常并正确关闭文件句柄,我认为您不能使用常规枚举快捷方式(迭代器块,yield-return/yield-打破).

相反,只需执行编译器为您完成的操作并添加一些:

通过自己实现IEnumerator,您还可以添加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
  }
}
Run Code Online (Sandbox Code Playgroud)

我没有对此进行测试,但我认为它很接近.

像这样使用:

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

编辑:我完全忘记添加try-catch块放置.哎呀.


Fem*_*ref 9

您不能yield在try/catch块中使用构造.将try块限制为可以抛出的代码,而不是全部代码.如果你不能做到这一点,那你就不走运了 - 你需要进一步抓住它.

  • 支持超级明智的“将 try 块限制为可能抛出异常的代码。” (2认同)
  • 这里值得注意的是,如果您只是将可能抛出的代码移至另一个方法并尝试包装对该方法的调用,您可能*不会*捕获错误,因为生成器是异步执行的。换句话说,代码在抛出时已移出 try/catch 块,因此 catch 块不再位于实际接收错误的范围内。 (2认同)

Eri*_*bes 9

编辑 - 由于评论中详细说明的原因,这个答案实际上是不正确的 - "只有枚举器生成被包装,而不是迭代本身." - 但我在这里留下这个答案,作为一个例子,说明有时可能看起来有效的东西并不是由于语言的错综复杂.

把它看作一个警示故事 - 我要感谢你们.=)


这是一个选项 - 将您的方法分为两种方法,一种是公共方法,另一种是私有方式.public方法是一个包装器(使用try/catch)围绕私有方法的调用,这是您的生成器.例如:

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

private IEnumerable<string> _yourFunction(...)
{
    // Your code here
}
Run Code Online (Sandbox Code Playgroud)

这将允许您的用户依赖具有内置异常处理的生成器.此外,您可以在公共方法中对输入执行更多验证,由于输入错误而根据需要抛出任何异常,并在调用方法时立即执行这些验证,而不是等待第一次枚举枚举.

  • 这很好并且都是正确的,但我认为重要的是要为读者注意,只有枚举器生成被包装,而不是迭代本身.如果文件在迭代期间突然不可用或者首先无法打开,则会向使用代码抛出异常.如果您尝试在生成器中打开流,那么您将无法在其范围的末尾正确处置它. (7认同)

xto*_*ofl 5

看看这个问题.你可以yield break在特殊情况下,yield valuetry/catch条款之后.我担心性能,但据信try在没有异常的情况下没有性能影响.