处理读取在范围外定义的流的StreamReader?

时间:2013-07-17 13:36:37

标签: c# stream dispose

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

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

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

这是我的实用方法。它的目标是读取一个Stream,并将其内容作为字符串返回,无论数据如何编码:

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

2 个答案:

答案 0 :(得分:1)

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

// 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清理它(因为具有填充工具IDisposable的整个点是为了避免这种情况开始)。由于这是一个接受Stream作为输入的库函数,因此假设方法使用者将创建流,因此,正如您所指出的那样,也有责任最终关闭流。但对于敏感资源而言,如果您担心确保清理,则反转有时是一种有用的技术。

答案 1 :(得分:0)

只有在对它们调用Dispose时,StreamReader才会关闭/处置它们的基础流。如果读者/作者只是垃圾收集,他们不会处理流。

相关问题