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

C# 处理读取范围外定义的流的StreamReader?,c#,stream,dispose,C#,Stream,Dispose,在一个实用方法中,它接受一个Stream参数,我依靠一些StreamReader来分析数据 我不想在我的方法中关闭传入流。我想让调用方方法决定如何处理流 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 ope

在一个实用方法中,它接受一个
Stream
参数,我依靠一些
StreamReader
来分析数据

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

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();
}
不处理打开的
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);
    }
}

可以使用闭包反转控件。也就是说,创建如下方法:

// 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);
    }
}
更新


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

您可以使用闭包反转控制。也就是说,创建如下方法:

// 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);
    }
}
更新


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

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

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

实际上,我的实用程序方法是一个通用库,而流的打开在我的程序中。我不确定是否理解您的代码片段,因为StreamReader的逻辑被移动到了程序中……其动机基本上是拥有一个单独的方法来处理打开和关闭流的实际工作,而流打开时需要完成的实际工作是单独定义和传递的。这确保了流从一开始就不必“泄漏”到其他方法中。每当GC接近流时,让流自行关闭似乎是个坏主意。当然,您也可以将
StreamReader
重新安排为存在于
StreamWork
函数中。关键的一点是,你不会泄露方法的信息流。当我重读你的问题时,我认为你颠倒了责任。(见我的更新问题)。实际上,如果我按照你的方向去做,我最终会得到类似(在程序中):
var with openedstream=newaction(a=>{using(var stream=File.OpenRead(@“c:\temp.txt”){a(stream);};MyUtility.GetStringAutoDetectEncoding(with openedstream,stream);
以及在方法本身:
void GetStringAutoDetectEncoding(操作a,流s){var进程=新操作((流)=>/*处理流*/);a(进程);}
。很难看,如果需要,您可以将所有这些因素考虑到一个单独的方法中。只要签名与上面的匹配,比如说,
操作
,并且仍然将其传递到方法中。查看您的更新,我发现您正在使用块调用
中的下游方法,这是有意义的。我认为您只是在继续让流挂起,直到GC最终决定关闭它。只要您有意地处置/关闭流(理想情况下在
中使用
块,如上所述)实际上,这是一个库函数,你可以考虑使用库的其他人是否有自己关闭流的规则。通过反转控制,你可以确保这总是发生,无论如何,通过接受流YO。U是对消费者负责的。除了流,我也用数据库连接进行这种倒置。但是,最终它只是一个偏好。至少要考虑一些。实际上,我的实用方法是一个通用的库,而流的打开在我的程序中。我不确定理解你的合作。当StreamReader的逻辑被移动到程序中时,de snippet的动机基本上是要有一个单独的方法来处理打开和关闭流的实际工作,而流打开时需要完成的实际工作是单独定义和传递的。这确保流永远不必“泄漏”到oth中一开始是er方法。每当GC接近流时,让流自行关闭似乎不是一个好主意。当然,您可以重新安排
StreamReader
,使其也存在于
StreamWork
函数中。关键点是不要将流泄漏出方法。当我