Value/Reference Types - Oooooops!

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Jerry Morton

    Value/Reference Types - Oooooops!

    Hi,
    I'm new to C# and have run into a problem of my own making! I
    originally took from the documentation that as long as I didn't use
    the "ref" keyword in method declarations, that everything would be
    passed "by value". I now believe I was incorrect and that it's only
    types like int, bool, etc. that behave like that? i.e. things like
    XmlDocument are always passed by reference in method calls?

    I was writing a "wrapper" class around a PRIVATE XmlDocument but
    because of the above lack of understanding, I don't think it's as
    "encapsulat ed" as I thought it was!

    e.g. in "MyWrapper" class:

    -------------------------------------------------
    ....
    private XmlDocument mvar_TheDocumen t = new XmlDocument(); //note
    "private"
    ....
    public MyWrapper()
    {
    mvar_TheDocumen t.Load("some path");
    }

    public XmlNode GetNodeByID(str ing sID)
    {
    return mvar_TheDocumen t.SelectSingleN ode("/blah blah find the
    node");
    }
    ....
    -------------------------------------------------


    So, on some WebForm I now say:

    ....
    MyClass objMyClass = new MyClass();
    XmlNode objNode = objMyClass.GetN odeByID(sSomeID );
    ....
    objNode.InnerXm l = string.empty;
    ....

    !!! Guess what? :-) The node in "mvar_TheDocume nt" gets cleared! Not
    what I had in mind - I want MyClass to be the only place with code
    that can update that XmlDocument.
    The problem is worse because MyClass persists for a long time, over
    many calls - so it's likely that I'm going to make a "coding mistake"
    at some point and inadvertantly modify my core document.

    What's the best thing to do (apart from sending me on a training
    course!)? I assume I have to return a "copy" of the node somehow? How
    does this impact memory management - does it get cleaned up once
    nothing needs the "copy" any more or do I have to set "objCopy = null"
    everywhere now?

    Obviously, I've also misunderstood the "private" member concept - even
    though the XmlDocument is "private", there's no runtime error when
    "external" code indirectly modifies it like that? Wow! I'm way off
    with this stuff!

    Thanks for any help!
  • Tom Dacon

    #2
    Re: Value/Reference Types - Oooooops!

    When you pass (by value) a reference to an object, the called method gets
    exactly that: a copy of the reference to the object. Since it was passed by
    value, the called method can't modify the original reference (the one in the
    calling method's scope), but it can change the state of the object that the
    argument refers to, providing the object has any public members that can
    change its state.

    If, on the other hand, you pass a reference BY REFERENCE, the called method
    is looking directly at the calling method's variable, and could change it.
    So when the method returns, the calling method might find that the variable
    now points to a different object.

    Clear as mud?
    Tom Dacon
    Dacon Software Consulting

    "Jerry Morton" <JerryMorton233 @mail.com> wrote in message
    news:11814a99.0 410081127.718d9 6dc@posting.goo gle.com...[color=blue]
    > Hi,
    > I'm new to C# and have run into a problem of my own making! I
    > originally took from the documentation that as long as I didn't use
    > the "ref" keyword in method declarations, that everything would be
    > passed "by value". I now believe I was incorrect and that it's only
    > types like int, bool, etc. that behave like that? i.e. things like
    > XmlDocument are always passed by reference in method calls?
    >
    > I was writing a "wrapper" class around a PRIVATE XmlDocument but
    > because of the above lack of understanding, I don't think it's as
    > "encapsulat ed" as I thought it was!
    >
    > e.g. in "MyWrapper" class:
    >
    > -------------------------------------------------
    > ...
    > private XmlDocument mvar_TheDocumen t = new XmlDocument(); //note
    > "private"
    > ...
    > public MyWrapper()
    > {
    > mvar_TheDocumen t.Load("some path");
    > }
    >
    > public XmlNode GetNodeByID(str ing sID)
    > {
    > return mvar_TheDocumen t.SelectSingleN ode("/blah blah find the
    > node");
    > }
    > ...
    > -------------------------------------------------
    >
    >
    > So, on some WebForm I now say:
    >
    > ...
    > MyClass objMyClass = new MyClass();
    > XmlNode objNode = objMyClass.GetN odeByID(sSomeID );
    > ...
    > objNode.InnerXm l = string.empty;
    > ...
    >
    > !!! Guess what? :-) The node in "mvar_TheDocume nt" gets cleared! Not
    > what I had in mind - I want MyClass to be the only place with code
    > that can update that XmlDocument.
    > The problem is worse because MyClass persists for a long time, over
    > many calls - so it's likely that I'm going to make a "coding mistake"
    > at some point and inadvertantly modify my core document.
    >
    > What's the best thing to do (apart from sending me on a training
    > course!)? I assume I have to return a "copy" of the node somehow? How
    > does this impact memory management - does it get cleaned up once
    > nothing needs the "copy" any more or do I have to set "objCopy = null"
    > everywhere now?
    >
    > Obviously, I've also misunderstood the "private" member concept - even
    > though the XmlDocument is "private", there's no runtime error when
    > "external" code indirectly modifies it like that? Wow! I'm way off
    > with this stuff!
    >
    > Thanks for any help![/color]


    Comment

    • Jerry Morton

      #3
      Re: Value/Reference Types - Oooooops!

      Tom - I read your reply (twice :-) and I do now understand. It's
      certainly an interesting nuance.

      I found that if I return a "copy" of my data I can avoid the problem,
      however I still have a problem with "XmlNode":

      e.g.

      -------------------------------------------------
      ....
      private XmlDocument mvar_TheDocumen t = new XmlDocument(); //note
      "private"
      ....
      public MyWrapper()
      {
      mvar_TheDocumen t.Load("some path");
      }
      public XmlDocument GetDocument()
      {
      //The following returns a "copy" of the document - the "private
      mvar_TheDocumen t" remains untouchable outside this object.
      XmlDocument objNewDoc = new XmlDocument();
      objNewDoc.LoadX ml(mvar_TheDocu ment.OuterXml);
      return objNewDoc;
      }

      public XmlNode GetNodeByID(str ing sID)
      {
      //How do I return a "copy" of an XmlNode - The following line gets a
      compiler error: "Cannot create an instance of the abstract class or
      interface 'System.Xml.Xml Node'"
      XmlNode objNewNode = new XmlNode();
      ...
      }
      ....
      -------------------------------------------------

      How do I do this for "XmlNode". Is this "copy" method even the right
      way to do all this?

      Although I understand HOW it happens, I'm still not clear WHY .NET
      allows a "private" member variable to be accessed this way. Seems
      inconsistent?

      Thanks.

      "Tom Dacon" <tdacon@communi ty.nospam> wrote in message news:<#XTpGRYrE HA.2596@TK2MSFT NGP12.phx.gbl>. ..[color=blue]
      > When you pass (by value) a reference to an object, the called method gets
      > exactly that: a copy of the reference to the object. Since it was passed by
      > value, the called method can't modify the original reference (the one in the
      > calling method's scope), but it can change the state of the object that the
      > argument refers to, providing the object has any public members that can
      > change its state.
      >
      > If, on the other hand, you pass a reference BY REFERENCE, the called method
      > is looking directly at the calling method's variable, and could change it.
      > So when the method returns, the calling method might find that the variable
      > now points to a different object.
      >
      > Clear as mud?
      > Tom Dacon
      > Dacon Software Consulting
      >[/color]

      Comment

      • Martin Honnen

        #4
        Re: Value/Reference Types - Oooooops!



        Jerry Morton wrote:

        [color=blue]
        > public XmlNode GetNodeByID(str ing sID)
        > {
        > //How do I return a "copy" of an XmlNode - The following line gets a
        > compiler error: "Cannot create an instance of the abstract class or
        > interface 'System.Xml.Xml Node'"
        > XmlNode objNewNode = new XmlNode();[/color]

        You can clone nodes in the DOM e.g.
        node.CloneNode( true)
        to create a clone. However that cloned node still belongs to the same
        ownerDocument as node so I can currently not assess whether that is of
        any help to you.
        Maybe you can explain in more detail how you want to consume the node
        returned by that method.

        --

        Martin Honnen

        Comment

        • Jerry Morton

          #5
          Re: Value/Reference Types - Oooooops!

          Hi Martin,
          I would prefer that the node returned from my function had no
          relationship to the private XmlDocument. Can XmlNodes even exist in
          isolation like this? i.e. without a parent XmlDocument? I appreciate
          that a CLONED node may not cause the same problems (like setting
          objNode.InnerXm l=string.empty! ) however this "cloning" does sound like
          a "memory leak waiting to happen"? This object persists for a long
          time too...

          The purpose of all this is to completely encapsulate a PRIVATE
          XmlDocument so that it can't be modified by code outside the class. I
          can't be the first person who want to do this :-)

          I still can't believe that a private member variable behaves this way.
          However, if it does then I can live with making "copies" of the
          document to pass back from functions. But does this mean that when I
          only want to pass one node of the document, I actually have to pass
          back an XmlDocument containing only that node?

          Martin Honnen <mahotrash@yaho o.de> wrote in message news:<eO5mqDfrE HA.3172@TK2MSFT NGP10.phx.gbl>. ..[color=blue]
          > Jerry Morton wrote:
          >
          >[color=green]
          > > public XmlNode GetNodeByID(str ing sID)
          > > {
          > > //How do I return a "copy" of an XmlNode - The following line gets a
          > > compiler error: "Cannot create an instance of the abstract class or
          > > interface 'System.Xml.Xml Node'"
          > > XmlNode objNewNode = new XmlNode();[/color]
          >
          > You can clone nodes in the DOM e.g.
          > node.CloneNode( true)
          > to create a clone. However that cloned node still belongs to the same
          > ownerDocument as node so I can currently not assess whether that is of
          > any help to you.
          > Maybe you can explain in more detail how you want to consume the node
          > returned by that method.[/color]

          Comment

          • Martin Honnen

            #6
            Re: Value/Reference Types - Oooooops!



            Jerry Morton wrote:

            [color=blue]
            > I would prefer that the node returned from my function had no
            > relationship to the private XmlDocument. Can XmlNodes even exist in
            > isolation like this? i.e. without a parent XmlDocument? I appreciate
            > that a CLONED node may not cause the same problems (like setting
            > objNode.InnerXm l=string.empty! ) however this "cloning" does sound like
            > a "memory leak waiting to happen"? This object persists for a long
            > time too...[/color]

            Inside the DOM object model implemented in .NET a node always belongs to
            its owner document, modelled in the
            node.OwnerDocum ent
            property. Thus even if you clone nodes with
            node.CloneNode( )
            you still have nodes associated with the owner document (and linked to
            it via node.OwnerDocum ent).
            As for the memory leak, managed code in .NET has garbage collection so
            once variables referencing nodes go out of scope they are available for
            garbage collection. I am not sure what else you could do (or even expect
            to happen behind the scenes) than cloning nodes.
            [color=blue]
            > The purpose of all this is to completely encapsulate a PRIVATE
            > XmlDocument so that it can't be modified by code outside the class. I
            > can't be the first person who want to do this :-)
            >
            > I still can't believe that a private member variable behaves this way.
            > However, if it does then I can live with making "copies" of the
            > document to pass back from functions. But does this mean that when I
            > only want to pass one node of the document, I actually have to pass
            > back an XmlDocument containing only that node?[/color]

            As said, you can use CloneNode to create a clone of your node but that
            clone is still linked to the original document via the OwnerDocument
            property. And of course while the original node might sit somewhere in
            the document tree (have a ParentNode) the cloned node doesn't have a
            ParentNode.

            --

            Martin Honnen

            Comment

            Working...