1

在一个接受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);
    }
}
4

2 回答 2

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目的是避免一开始就出现这种情况)。由于这是一个接受 aStream作为输入的库函数,因此假设方法使用者将创建流,因此正如您所指出的,它也有责任最终关闭流。但是对于您关心确保绝对清理的敏感资源,反转有时是一种有用的技术。

于 2013-07-17T13:45:36.187 回答
0

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

于 2013-07-17T13:47:10.317 回答