正在处理StreamReader,该StreamReader读取范围外定义的流

本文关键字:StreamReader 定义 范围 处理 读取 | 更新日期: 2023-09-27 18:22:44

在接受Stream参数的实用程序方法中,我依赖一些StreamReader来分析数据。

我不想关闭方法中的传入流。我想让调用者方法决定处理流。

不处理打开的StreamReader是否安全?我的意思是,它最终会被自动处理吗?它会导致内存泄漏吗?

这是我的实用方法。它的目标是读取流,并将其内容作为字符串返回,而不管数据是如何编码的:

    public static string GetStringAutoDetectEncoding(Stream data, out Encoding actualEncoding)
    {
        // 1. Is there a Bye Order Mask ?
        var candidateEncoding = DetectEncodingWithByteOrderMask(data);
        // 2a. No BOM, the data is either UTF8 no BOM or ANSI
        if (candidateEncoding == Encoding.Default)
        {
            var utf8NoBomEncoding = Encoding.GetEncoding("utf-8",new EncoderExceptionFallback(), new DecoderExceptionFallback());
            var positionBackup = data.Position;
            var sr = new StreamReader(data, utf8NoBomEncoding);
            try
            {
                // 3. Try as UTF8 With no BOM
                var result = sr.ReadToEnd(); // will throw error if not UTF8
                actualEncoding = utf8NoBomEncoding; // Probably an UTF8 no bom string
                return result;
            }
            catch (DecoderFallbackException)
            {
                // 4. Rewind the stream and fallback to ASNI
                data.Position = positionBackup;
                var srFallback = new StreamReader(data, candidateEncoding);
                actualEncoding = candidateEncoding;
                return srFallback.ReadToEnd(); ;
            }
        }
        // 2b. There is a BOM. Use the detected encoding
        else
        {
            var sr = new StreamReader(data, candidateEncoding);
            actualEncoding = candidateEncoding;
            return sr.ReadToEnd(); ;
        }
    }

然后,我可以在这样的一些方法:

void Foo(){
    using(var stream = File.OpenRead(@"c:'somefile")) {
        Encoding detected;
        var fileContent = MyUtilityClass.GetStringAutoDetectEncoding(stream, detected);
        Console.WriteLine("Detected encoding: {0}", encoding);
        Console.WriteLine("File content: {0}", fileContent);
    }
}

正在处理StreamReader,该StreamReader读取范围外定义的流

您可以使用闭包来反转控件。也就是说,创建一个这样的方法:

// This method will open the stream, execute the streamClosure, and then close the stream.
public static String StreamWork(Func<Stream, String> streamClosure) {
    // Set up the stream here.
    using (Stream stream = new MemoryStream()) { // Pretend the MemoryStream is your actual stream.
        // Execute the closure. Return it's results.
        return streamClosure(stream);
    }
}

其负责打开/关闭该方法内的流。

然后,您只需将所有需要该流的代码封装到Func<Stream, String>闭包中,并将其传入。StreamWork方法将打开该流,执行您的代码,然后关闭该流。

public static void Main()
{
    // Wrap all of the work that needs to be done in a closure.
    // This represents all the work that needs to be done while the stream is open.
    Func<Stream, String> streamClosure = delegate(Stream stream) {
        using (StreamReader streamReader = new StreamReader(stream)) {
            return streamReader.ReadToEnd();
        }
    };
    // Call StreamWork. This method handles creating/closing the stream.
    String result = StreamWork(streamClosure);
    Console.WriteLine(result);
    Console.ReadLine();
}

更新

当然,正如下面的评论中所提到的,这种反演方法是一个偏好问题。关键是要确保流是关闭的,而不是让它四处浮动,直到GC将其清理干净(因为让stuff实现IDisposable的全部目的是从一开始就避免这种情况)。由于这是一个接受Stream作为输入的库函数,因此假设方法使用者将创建流,因此正如您所指出的,也有责任最终关闭流。但对于敏感资源,如果您担心确保绝对进行清理,反转有时是一种有用的技术。

StreamReader只有在您对其调用dispose时才关闭/处置其底层流。如果读取器/写入器只是垃圾收集,则它们不会处理流。