首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >有效使用using-语句(不在MSDN中)

有效使用using-语句(不在MSDN中)
EN

Stack Overflow用户
提问于 2015-05-01 12:07:09
回答 5查看 347关注 0票数 2

我已经读过相应的“医生-页”了,但我的问题仍然没有答案。假设我想在while循环中使用disposable Object,如下所示:

代码语言:javascript
复制
StreamReader reader;
while (!ShouldStop)
{
    using (reader = new StreamReader(networkStream))
    {
         // Some code here...    
    }
}

如何看到,我声明StreamReade readerusing-statement之外。我通常是这样做的,因为我认为,然后将内存的一个区域分配给那个StreamReader,只为分配一次。当我像这样使用use -语句时:

代码语言:javascript
复制
while (!ShouldStop)
{
    using (StreamReader reader = new StreamReader(networkStream))
    {
        // Some code here...            
    }
}

我认为StreamReader-object的内存分配是连续的,因此的效率要低得多&改进的。但是,我不知道using-statement的第一次使用是否定期调用实例的Dispose()-function。那么,using-statement的第一个用法与第二个用法相同吗?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2015-05-01 13:10:50

我通常是这样做的,因为我认为只有一次就为该StreamReader分配了一个内存区域。

这就是你要犯错的地方。

局部变量在使用期间占用一定数量的堆栈空间,对象在new上占用一定数量的堆空间。这不会改变的。

实际上,编译器最终会占用更多的堆栈空间。只需比较两种方法的IL使用每种方法。我们将使用这个C#:

代码语言:javascript
复制
private static string LastLine1(NetworkStream networkStream)
{
    string last = null;
    StreamReader reader;
    while(!ShouldStop)
    {
        using(reader = new StreamReader(networkStream))
        {
            string line = reader.ReadLine();
            if(line != null)
                last = line;
        }
    }
    return last;
}
private static string LastLine2(NetworkStream networkStream)
{
    string last = null;
    while(!ShouldStop)
    {
        using(StreamReader reader = new StreamReader(networkStream))
        {
            string line = reader.ReadLine();
            if(line != null)
                last = line;
        }
    }
    return last;
}

我们得到了这个CIL:

代码语言:javascript
复制
.method private hidebysig static 
    string LastLine1 (
        class [System]System.Net.Sockets.NetworkStream networkStream
    ) cil managed 
{
    .maxstack 2
    .locals init (
        [0] string,
        [1] class [mscorlib]System.IO.StreamReader,
        [2] string,
        [3] class [mscorlib]System.IO.StreamReader
    )

    IL_0000: ldnull
    IL_0001: stloc.0
    IL_0002: br.s IL_0025
    IL_0004: ldarg.0
    IL_0005: newobj instance void [mscorlib]System.IO.StreamReader::.ctor(class [mscorlib]System.IO.Stream)
    IL_000a: dup
    IL_000b: stloc.1
    IL_000c: stloc.3
    .try
    {
        IL_000d: ldloc.1
        IL_000e: callvirt instance string [mscorlib]System.IO.TextReader::ReadLine()
        IL_0013: stloc.2
        IL_0014: ldloc.2
        IL_0015: brfalse.s IL_0019

        IL_0017: ldloc.2
        IL_0018: stloc.0

        IL_0019: leave.s IL_0025
    }
    finally
    {
        IL_001b: ldloc.3
        IL_001c: brfalse.s IL_0024

        IL_001e: ldloc.3
        IL_001f: callvirt instance void [mscorlib]System.IDisposable::Dispose()

        IL_0024: endfinally
    }
    IL_0025: call bool Demonstrate.Program::get_ShouldStop()
    IL_002a: brfalse.s IL_0004

    IL_002c: ldloc.0
    IL_002d: ret
}

.method private hidebysig static 
    string LastLine2 (
        class [System]System.Net.Sockets.NetworkStream networkStream
    ) cil managed 
{
    .maxstack 1
    .locals init (
        [0] string,
        [1] class [mscorlib]System.IO.StreamReader,
        [2] string
    )

    IL_0000: ldnull
    IL_0001: stloc.0
    IL_0002: br.s IL_0023
    IL_0004: ldarg.0
    IL_0005: newobj instance void [mscorlib]System.IO.StreamReader::.ctor(class [mscorlib]System.IO.Stream)
    IL_000a: stloc.1
    .try
    {
        IL_000b: ldloc.1
        IL_000c: callvirt instance string [mscorlib]System.IO.TextReader::ReadLine()
        IL_0011: stloc.2
        IL_0012: ldloc.2
        IL_0013: brfalse.s IL_0017

        IL_0015: ldloc.2
        IL_0016: stloc.0

        IL_0017: leave.s IL_0023
    }
    finally
    {
        IL_0019: ldloc.1
        IL_001a: brfalse.s IL_0022

        IL_001c: ldloc.1
        IL_001d: callvirt instance void [mscorlib]System.IDisposable::Dispose()

        IL_0022: endfinally
    }

    IL_0023: call bool Demonstrate.Program::get_ShouldStop()
    IL_0028: brfalse.s IL_0004

    IL_002a: ldloc.0
    IL_002b: ret
}

(严格地说,这两种方法确实应该产生相同的代码,但事实是它们没有,您的方法是使用堆栈空间稍微长一些,稍微大一点)。

因为C#编译器无法优化您在使用之外拥有的reader,所以实际上是您的方法导致占用额外的堆栈空间用于另一个reader副本。

如果您不熟悉CIL,请比较一下ILSpy如何再次将它们解压缩回C#:

代码语言:javascript
复制
private static string LastLine1(NetworkStream networkStream)
{
    string result = null;
    while (!Program.ShouldStop)
    {
        StreamReader streamReader2;
        StreamReader streamReader = streamReader2 = new StreamReader(networkStream);
        try
        {
            string text = streamReader.ReadLine();
            if (text != null)
            {
                result = text;
            }
        }
        finally
        {
            if (streamReader2 != null)
            {
                ((IDisposable)streamReader2).Dispose();
            }
        }
    }
    return result;
}

private static string LastLine2(NetworkStream networkStream)
{
    string result = null;
    while (!Program.ShouldStop)
    {
        using (StreamReader streamReader = new StreamReader(networkStream))
        {
            string text = streamReader.ReadLine();
            if (text != null)
            {
                result = text;
            }
        }
    }
    return result;
}

(这也可能减少了在将空检查转化为实际运行的机器代码时被优化的可能性)。

它的效率要低得多&香喷剂。但是,我不知道using-语句的第一次使用是否定期调用实例的dont ()-function。

无论是哪种方式,using都会称Dispose()为好的,但是您的浪费要稍微大一些,因此效率和性能要差一些。也许可以忽略不计,但您所避免的方法肯定不是像您所声称的那样“效率更低、性能更差”。

一般来说,保持你的范围紧。主要原因是,不再在作用域中的变量是一个变量,您不能再做错误,甚至需要考虑,所以您将拥有更干净的代码,bug更少,bug更容易发现。第二个原因是,有几种情况,如这种情况,更大的范围导致了非常多的浪费代码。

现在,将赋值放在循环之外确实可以更好地执行。如果您的代码可以使用:

代码语言:javascript
复制
using(var reader = new StreamReader(networkStream))
  while(!ShouldStop)
  {
    // do stuff
  }

这样就可以节省堆搅动,而且最重要的是,减少了,因此,如果它能够工作,这将是一个改进。

然而,声明不会让做任何操作,所以在循环之外让它们不起作用是没有帮助的,有时还会有轻微的阻碍。

票数 2
EN

Stack Overflow用户

发布于 2015-05-01 12:10:05

你不能通过这样做来节省任何记忆:

代码语言:javascript
复制
StreamReader reader;
while (!ShouldStop)
{
    using (reader = new StreamReader(networkStream))
    {
         // Some code here...    
    }
}

您已经在循环之外声明了变量,但是仍然在每次迭代中创建一个新对象。

票数 3
EN

Stack Overflow用户

发布于 2015-05-01 12:32:54

这两者之间没有什么区别,因为每当您调用new时,都会考虑分配一块内存。using语句是这个语句的语法糖(我的意思是the语句转换为这个):

代码语言:javascript
复制
        StreamReader reader = new StreamReader(networkStream)

        try
        {
            //some codes here
        }
        finally
        {
            if (reader!=null)
               ( (IDisposable) reader).Dispose();
        }

因此,在这两种情况下,内存释放在最后,并再次分配,如果需要的话。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29986559

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档