none
Why is there the difference between the two snippets of codes? RRS feed

  • Question

  • Plz read my codes very carefully and do answering to my questions:)

    [PART1]

    namespace CSharp
    {
        class MyDisposible : IDisposable
        {
            private string s = "abc";
     
            public string S
            {
                get { return s; }
                set { s = value; }
            }
     
            public void Dispose()
            {
                s = null;
            }
        }
     
        class Program
        {
            static MyDisposible Fun()
            {
                using (MyDisposible m = new MyDisposible())
                {
                    return m;
                }
            }
            static void Main(string[] args)
            {
                Console.WriteLine(Fun().S.Length);
            }
        }
    }

    [PART2]

    namespace CSharp
    {
        class MyDisposible : IDisposable
        {
            private string s = "abc";
     
            public string S
            {
                get { return s; }
                set { s = value; }
            }
     
            public void Dispose()
            {
                Console.WriteLine("I've disposed……");
                s = null;
            }
        }
     
        class Program
        {
            static string Fun()
            {
                using (MyDisposible m = new MyDisposible())
                {
                    return m.S;
                }
            }
            static void Main(string[] args)
            {
                Console.WriteLine(Fun().Length);
            }
        }
    }

    For PART1, Dispose will be called before return, so it will throw an exception.

    But for PART2, Dispose will also be called before return, but WHY DOES IT STLL WORK PROPERLY TO GET STRING'S LENGTH WITHOUT ANY EXCEPTIONS?

    Friday, August 3, 2012 9:29 AM

Answers

  • PART1 roughly translates to this.

    string s = "abc";
    s = null;
    s.Length; // null ref!

    PART2 is like this.

    string s = "abc";
    string t = s;
    s = null;
    t.Length; // no null ref!

    The Fun() method translates to this. Dispose is always run before exiting the method.

    static MyDisposible Fun()
    {
      MyDisposible m = new MyDisposible();
      m.Dispose();
      return m;
    }

    Using statement ensure that Dispose() is called when it goes out of scope, which is when returning from the method. Using return from inside does not change it.

    Friday, August 3, 2012 2:27 PM
    Moderator
  • OK,I think the problem may be this happening——

    using(MyDisposible mc = new MyDisposible()) 
    { 
       return mc.S; 
    } 
     
    //This will be converted to this following: 
     
    string $$s = null; 
    mc=null; 
    try 
    { 
       mc = new MyDisposible(); 
       $$s = mc.S; 
    } 
    finally 
    { 
       mc.Dispose(); 
       return $$s; 
    }
    • Marked as answer by TimoYang Saturday, August 4, 2012 3:33 AM
    Saturday, August 4, 2012 3:32 AM

All replies

  • In PART2 you return a reference to the string. Setting s to null does not effect the reference you returned, just the member variable in MyDisposable.
    Friday, August 3, 2012 11:37 AM
    Moderator
  • I am not getting exceptions with this code.

        class MyDisposible : IDisposable
        {
            private string s = "abc";
    
            public string S
            {
                get { return s; }
                set { s = value; }
            }
    
            private object o = new object();
            public object O
            {
                get { return o; }
                set { o = value; }
            }
    
            public void Dispose()
            {
                Console.WriteLine("I am disposing.");
                s = null;
                o = null;
                Console.WriteLine("I have disposed.");
            }
    
            public static object DisposableTest()
            {
                using ( MyDisposible m = new MyDisposible() )
                {
                    return m;  // no exception
                }
            }
        }

    Rudy   =8^D


    Mark the best replies as answers. "Fooling computers since 1971."

    http://thesharpercoder.blogspot.com/

    Friday, August 3, 2012 1:33 PM
  • I am not getting exceptions with this code.

    I believe the exception would be when trying to get the Length property of the member S after it has been disposed. NullReferenceException.
    Friday, August 3, 2012 1:36 PM
    Moderator
  • I am not getting exceptions with this code.

    I believe the exception would be when trying to get the Length property of the member S after it has been disposed. NullReferenceException.

    Ah, good call.  I changed my test.  Notice that I have two Console.WriteLine calls in my original Dispose method.  They are both called prior to writing out the string's value in the code below.  The object, "obj", gets disposed.  More accurately, it becomes a candidate for the GC.

            static void DisposableTest()
            {
                MyDisposible obj = MyDisposible.DisposableTest() as MyDisposible;
                string s = obj.S; // no exception;
                Console.WriteLine("string = " + s);
                object o = obj.O;  // no exception
                int len = obj.S.Length;  // variable S is null;
            }

    But, if I comment out the line in Dispose, I get no exceptions, and I still see both of my Dispose messages and I see "abc".

            public void Dispose()
            {
                Console.WriteLine("I am disposing.");
                //s = null;
                o = null;
                Console.WriteLine("I have disposed.");
            }

    I would conclude that the null reference is coming from setting the variable to null.

    But, if


    Mark the best replies as answers. "Fooling computers since 1971."

    http://thesharpercoder.blogspot.com/


    • Edited by Rudedog2 Friday, August 3, 2012 2:10 PM
    Friday, August 3, 2012 2:09 PM
  • PART1 roughly translates to this.

    string s = "abc";
    s = null;
    s.Length; // null ref!

    PART2 is like this.

    string s = "abc";
    string t = s;
    s = null;
    t.Length; // no null ref!

    The Fun() method translates to this. Dispose is always run before exiting the method.

    static MyDisposible Fun()
    {
      MyDisposible m = new MyDisposible();
      m.Dispose();
      return m;
    }

    Using statement ensure that Dispose() is called when it goes out of scope, which is when returning from the method. Using return from inside does not change it.

    Friday, August 3, 2012 2:27 PM
    Moderator
  • In PART2 you return a reference to the string. Setting s to null does not effect the reference you returned, just the member variable in MyDisposable.
    But Please compare my snippet 1 and snippet 2——If u think return is executed before dispose(OK I also think so),but sample 1 shows when u return an instance of MyDispose class the "S" will be disposed to be null;but if I directly return this, it won't happen;but in my mind,no matter what you've returned,in the end Dispose will be called and "S" will be finally set to null……
    Saturday, August 4, 2012 1:39 AM
  • I am not getting exceptions with this code.

        class MyDisposible : IDisposable
        {
            private string s = "abc";
    
            public string S
            {
                get { return s; }
                set { s = value; }
            }
    
            private object o = new object();
            public object O
            {
                get { return o; }
                set { o = value; }
            }
    
            public void Dispose()
            {
                Console.WriteLine("I am disposing.");
                s = null;
                o = null;
                Console.WriteLine("I have disposed.");
            }
    
            public static object DisposableTest()
            {
                using ( MyDisposible m = new MyDisposible() )
                {
                    return m;  // no exception
                }
            }
        }

    Rudy   =8^D


    Mark the best replies as answers. "Fooling computers since 1971."

    http://thesharpercoder.blogspot.com/

    Hi:)

    I don't want to avoid throwing exceptions,I did do to throw exceptions on purpose to prove it that Dispose will happen after return and set the instance inside “using……”statement to null……So "S" should be set to null,but no in the end?Why?

    Saturday, August 4, 2012 1:41 AM
  • The Fun() method translates to this. Dispose is always run before exiting the method.

    static MyDisposible Fun()
    {
      MyDisposible m = new MyDisposible();
      m.Dispose();
      return m;
    }

    Sorry but this only means you are wrong now——if "Dispose" is called, so "S" will be immediately set to null, even if I return m.S, this will be null value……

    And using……should be changed to try……finally……,Yours isn't right……:(

    Saturday, August 4, 2012 1:45 AM
  • "s" is null for the instance of "MyDisposable" after the Dipose call in [PART2] - that's true.
    But the value of the field "s" - the string "abc" - still has another reference
    so it is not removed from memory through the GC -
    and this other reference is not affected by the Dispose call.

    This reference is the return value of the method "Fun"
    which is a local variable created before the call of the Dispose method.
    After the using block is exited, the method returns and the
    local reference to "abc" gets copied into the actual parameter of Console.WriteLine.
    So "abc" is still referenced and gets printed out.

    Note that setting a variable to null should not be confused with releasing its memory.
    It's not a delete or free statement we know from C/C++.
    So by setting "s" to null its memory is not deallocated. It just has one reference less.
    Note also that most often the Dispose method is called to release
    unmanaged resources such as handles, which usually get closed
    by CloseHandle, FindClose, or whatsoever.
    If you'd try to access a closed handle after the call to Dispose,
    your assumption that this should fail would be 100% correct.

    For reference types in .NET the mechanism of releasing memory
    is different - it's the reference count that matters.

    Chris






    Saturday, August 4, 2012 2:15 AM
  • OK,I think the problem may be this happening——

    using(MyDisposible mc = new MyDisposible()) 
    { 
       return mc.S; 
    } 
     
    //This will be converted to this following: 
     
    string $$s = null; 
    mc=null; 
    try 
    { 
       mc = new MyDisposible(); 
       $$s = mc.S; 
    } 
    finally 
    { 
       mc.Dispose(); 
       return $$s; 
    }
    • Marked as answer by TimoYang Saturday, August 4, 2012 3:33 AM
    Saturday, August 4, 2012 3:32 AM
  • "s" is null for the instance of "MyDisposable" after the Dipose call in [PART2] - that's true.
    But the value of the field "s" - the string "abc" - still has another reference

    that's not the reason, because CLR has done something different from what you can see in the original codes……Haha^_^
    Saturday, August 4, 2012 3:34 AM
  • In PART2 you return a reference to the string. Setting s to null does not effect the reference you returned, just the member variable in MyDisposable.

    But Please compare my snippet 1 and snippet 2——If u think return is executed before dispose(OK I also think so),but sample 1 shows when u return an instance of MyDispose class the "S" will be disposed to be null;but if I directly return this, it won't happen;but in my mind,no matter what you've returned,in the end Dispose will be called and "S" will be finally set to null……

    I did compare the snippets and they work exactly as you wrote them and in the first case you will get a reference exception and in the second not. This is because in the first case you return the object itself, which gets the string set to null. In the second case you return the reference to the string itself and it does not crash.

    You do not set the string to null in your dispose method, you set the reference to the string to null. The string does not change its value and that is why in the second case it works.

    Saturday, August 4, 2012 6:14 AM
    Moderator
  • The Fun() method translates to this. Dispose is always run before exiting the method.

    static MyDisposible Fun()
    {
      MyDisposible m = new MyDisposible();
      m.Dispose();
      return m;
    }

    Sorry but this only means you are wrong now——if "Dispose" is called, so "S" will be immediately set to null, even if I return m.S, this will be null value……

    And using……should be changed to try……finally……,Yours isn't right……:(


    I am not wrong on this, study the compilers IL output with a decompiler. It is a bit more advanced this simplified what will happen when doing a using statement.
    Saturday, August 4, 2012 6:16 AM
    Moderator
  • OK,I think the problem may be this happening——

    using(MyDisposible mc = new MyDisposible()) 
    { 
       return mc.S; 
    } 
     
    //This will be converted to this following: 
     
    string $$s = null; 
    mc=null; 
    try 
    { 
       mc = new MyDisposible(); 
       $$s = mc.S; 
    } 
    finally 
    { 
       mc.Dispose(); 
       return $$s; 
    }
    This is basically what I said earlier... not sure why you disagree with me.
    Saturday, August 4, 2012 6:18 AM
    Moderator
  • Haha, seems you enjoy coding, lol. Me too, haha.

    Chris
    Sunday, August 5, 2012 12:13 PM