Observation on object references

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Shailen Sukul

    Observation on object references

    Observed a weird behaviour with object references.
    See code listing below:

    using System;
    using System.Collecti ons.Generic;
    using System.Text;

    namespace PointerExceptio nTest
    {
    /*
    Desc: Apparent weird behaviour of encapsulated reference types
    getting "lost" upon instance renewal.

    The following code example shows a basic implementation of the Chain
    of Responsibility design
    pattern, where the main class (Orchestrator) creates 2 instances of
    the Handler classes, (h1
    and h2) and chains them together. It then passes an event argument
    variable to h1 by calling
    its Process method. The args variable contains a data object that
    will be inspected after the
    chain is complete. It also contains a data variable that is passed as
    a reference to the args constructor.

    Handler1 simply changes a value if args.data.field 1
    Handler2 recreates a new instance of data and assigns it to args.data.

    We would expect Orchestration.a rgs.data to be updated to the new
    reference of data2 in Handler2 and that DOES happen.
    We would also expect Orchestration.d ata to be updated to the new
    reference of data2 in Handler2, but that DOES NOT happen!
    When the args.data gets assigned to a new data variable,
    Orchestration.d ata loses reference to the new value from then on.
    Being a pointer, one would expect it to be updated, but as the output
    clearly shows, it does not.

    */
    class Program
    {
    static void Main(string[] args)
    {
    Orchestrator o = new Orchestrator();
    o.Start();

    Console.ReadLin e();
    }
    }

    public class Orchestrator
    {
    IData data = new Data();
    IHandlerArgs args = null;

    HandlerBase h1 = new Handler1();
    HandlerBase h2 = new Handler2();

    public void Start()
    {
    args = new HandlerArgs(dat a);
    h1.successor = h2;
    h1.Process(args );

    Console.WriteLi ne(string.Forma t("args.data.fi eld1:
    {0}",args.data. field1));
    Console.WriteLi ne(string.Forma t("data.field 1: {0}", data.field1));
    }

    }

    /* Base class for Handlers */
    public abstract class HandlerBase
    {
    public abstract void Process(IHandle rArgs args);
    public HandlerBase successor = null;

    }

    /* Concrete instance of a handler */
    public class Handler1 : HandlerBase
    {
    public override void Process(IHandle rArgs args)
    {
    args.data.field 1 = "Handerl";
    if (successor != null)
    {
    successor.Proce ss(args);
    }
    }
    }

    /* Concrete instance of a handler */
    public class Handler2 : HandlerBase
    {
    public override void Process(IHandle rArgs args)
    {
    args.data = new Data();

    args.data.field 1 = "Hander2";
    if (successor != null)
    {
    successor.Proce ss(args);
    }
    }
    }


    /* Interface for the argument that is passed to the handler */
    public interface IHandlerArgs
    {
    IData data
    {
    get;
    set;
    }
    }

    /* Concrete instance of HandlerArgs */
    public class HandlerArgs : IHandlerArgs
    {
    public HandlerArgs(IDa ta data)
    {
    _data = data;
    }

    IData _data = null;
    public IData data
    {
    get
    {
    return _data;
    }
    set
    {
    _data = value;
    }
    }

    }

    /* Interface for the data packet that will be
    encapsulated in the HandlerArgs.
    */
    public interface IData
    {
    string field1
    {
    get;
    set;
    }
    }

    /* Concrete instance of a data packet */
    public class Data : IData
    {
    private string _field1 = "null";
    public string field1
    {
    get
    {
    return _field1;
    }
    set
    {
    _field1 = value;
    }
    }

    }

    }

    --
    Good luck!

    Shailen Sukul
    Architect
    (BSc MCTS, MCSD.Net MCSD MCAD)
    Ashlen Consulting Service P/L
    (http://www.ashlen.net.au)
  • Mark R. Dawson

    #2
    RE: Observation on object references

    Hi Shailen,
    if you run your code you get the following output o the command line:

    args.data.field 1: Handler2
    data.field1: Handler1

    To me that seems exactly what I would expect. Basically you Orchestrator
    object o initially creates a Data object data (we will call this d1) and
    passes in the reference to that to the args variable, at this point we know:
    o.args.data.fie ld1 == "null", where data refers to d1 instance.

    You then call processs and pass in the args variable, Handler1 changes the
    field1 value so:
    o.args.data.fie ld1 == "Handler1"

    everything is good so far, the args instance is passed to Handler2, at this
    point Handler2 creates a new instance of the Data object (call this d2) and
    passes that to the args object, so before we had:

    o.args.data -referring to d1 instance we created earlier.

    now we have o.args.data -refers to d2, and set the field value
    o.args.data.fie ld1 == "handler2"

    so back in the following code:
    public void Start()
    {
    args = new HandlerArgs(dat a);
    h1.successor = h2;
    h1.Process(args );

    Console.WriteLi ne(string.Forma t("args.data.fi eld1:
    {0}",args.data. field1));
    Console.WriteLi ne(string.Forma t("data.field 1: {0}", data.field1));
    }

    args.data refers to Data instance d2, so args.data.field 1 == "handler2" and
    your original data field d1 will still have a field value of "Handler1". The
    important point to note is when you did:

    public class Handler2 : HandlerBase
    {
    public override void Process(IHandle rArgs args)
    {
    args.data = new Data();

    you replaced what the args instance referenced. What were you expecting to
    happen?

    Mark.
    --



    "Shailen Sukul" wrote:
    Observed a weird behaviour with object references.
    See code listing below:
    >
    using System;
    using System.Collecti ons.Generic;
    using System.Text;
    >
    namespace PointerExceptio nTest
    {
    /*
    Desc: Apparent weird behaviour of encapsulated reference types
    getting "lost" upon instance renewal.
    >
    The following code example shows a basic implementation of the Chain
    of Responsibility design
    pattern, where the main class (Orchestrator) creates 2 instances of
    the Handler classes, (h1
    and h2) and chains them together. It then passes an event argument
    variable to h1 by calling
    its Process method. The args variable contains a data object that
    will be inspected after the
    chain is complete. It also contains a data variable that is passed as
    a reference to the args constructor.
    >
    Handler1 simply changes a value if args.data.field 1
    Handler2 recreates a new instance of data and assigns it to args.data.
    >
    We would expect Orchestration.a rgs.data to be updated to the new
    reference of data2 in Handler2 and that DOES happen.
    We would also expect Orchestration.d ata to be updated to the new
    reference of data2 in Handler2, but that DOES NOT happen!
    When the args.data gets assigned to a new data variable,
    Orchestration.d ata loses reference to the new value from then on.
    Being a pointer, one would expect it to be updated, but as the output
    clearly shows, it does not.
    >
    */
    class Program
    {
    static void Main(string[] args)
    {
    Orchestrator o = new Orchestrator();
    o.Start();
    >
    Console.ReadLin e();
    }
    }
    >
    public class Orchestrator
    {
    IData data = new Data();
    IHandlerArgs args = null;
    >
    HandlerBase h1 = new Handler1();
    HandlerBase h2 = new Handler2();
    >
    public void Start()
    {
    args = new HandlerArgs(dat a);
    h1.successor = h2;
    h1.Process(args );
    >
    Console.WriteLi ne(string.Forma t("args.data.fi eld1:
    {0}",args.data. field1));
    Console.WriteLi ne(string.Forma t("data.field 1: {0}", data.field1));
    }
    >
    }
    >
    /* Base class for Handlers */
    public abstract class HandlerBase
    {
    public abstract void Process(IHandle rArgs args);
    public HandlerBase successor = null;
    >
    }
    >
    /* Concrete instance of a handler */
    public class Handler1 : HandlerBase
    {
    public override void Process(IHandle rArgs args)
    {
    args.data.field 1 = "Handerl";
    if (successor != null)
    {
    successor.Proce ss(args);
    }
    }
    }
    >
    /* Concrete instance of a handler */
    public class Handler2 : HandlerBase
    {
    public override void Process(IHandle rArgs args)
    {
    args.data = new Data();
    >
    args.data.field 1 = "Hander2";
    if (successor != null)
    {
    successor.Proce ss(args);
    }
    }
    }
    >
    >
    /* Interface for the argument that is passed to the handler */
    public interface IHandlerArgs
    {
    IData data
    {
    get;
    set;
    }
    }
    >
    /* Concrete instance of HandlerArgs */
    public class HandlerArgs : IHandlerArgs
    {
    public HandlerArgs(IDa ta data)
    {
    _data = data;
    }
    >
    IData _data = null;
    public IData data
    {
    get
    {
    return _data;
    }
    set
    {
    _data = value;
    }
    }
    >
    }
    >
    /* Interface for the data packet that will be
    encapsulated in the HandlerArgs.
    */
    public interface IData
    {
    string field1
    {
    get;
    set;
    }
    }
    >
    /* Concrete instance of a data packet */
    public class Data : IData
    {
    private string _field1 = "null";
    public string field1
    {
    get
    {
    return _field1;
    }
    set
    {
    _field1 = value;
    }
    }
    >
    }
    >
    }
    >
    --
    Good luck!
    >
    Shailen Sukul
    Architect
    (BSc MCTS, MCSD.Net MCSD MCAD)
    Ashlen Consulting Service P/L
    (http://www.ashlen.net.au)

    Comment

    • Dave Sexton

      #3
      Re: Observation on object references

      Hi Shailen,

      The local "data" variable was never changed to point to the new reference.

      The behavior you are witnessing may be condensed into a simple example. This
      is what's happening in your code, minus all the bloat:

      IData data = new Data();
      IHandlerArgs args = new HandlerArgs(dat a);

      args.data.field 1 = "Handler 1";

      // Note: this assignment doesn't update the local "data" variable
      // to the new reference. After, you'll have references to two instances
      args.data = new Data();

      args.data.field 1 = "Handler 2";

      Console.WriteLi ne("Encapsulate d value: " + args.data.field 1);
      // Prints "Handler 2"

      Console.WriteLi ne("Local value: " + data.field1);
      // Prints "Handler 1" because args.data reference was still the same
      // as the local "data" variable

      --
      Dave Sexton

      "Shailen Sukul" <shane@ashlen.n et.auwrote in message
      news:14756EE3-F6D6-4523-879D-F5F008ED5645@mi crosoft.com...
      Observed a weird behaviour with object references.
      See code listing below:
      >
      using System;
      using System.Collecti ons.Generic;
      using System.Text;
      >
      namespace PointerExceptio nTest
      {
      /*
      Desc: Apparent weird behaviour of encapsulated reference types
      getting "lost" upon instance renewal.
      >
      The following code example shows a basic implementation of the Chain
      of Responsibility design
      pattern, where the main class (Orchestrator) creates 2 instances of
      the Handler classes, (h1
      and h2) and chains them together. It then passes an event argument
      variable to h1 by calling
      its Process method. The args variable contains a data object that
      will be inspected after the
      chain is complete. It also contains a data variable that is passed as
      a reference to the args constructor.
      >
      Handler1 simply changes a value if args.data.field 1
      Handler2 recreates a new instance of data and assigns it to args.data.
      >
      We would expect Orchestration.a rgs.data to be updated to the new
      reference of data2 in Handler2 and that DOES happen.
      We would also expect Orchestration.d ata to be updated to the new
      reference of data2 in Handler2, but that DOES NOT happen!
      When the args.data gets assigned to a new data variable,
      Orchestration.d ata loses reference to the new value from then on.
      Being a pointer, one would expect it to be updated, but as the output
      clearly shows, it does not.
      >
      */
      class Program
      {
      static void Main(string[] args)
      {
      Orchestrator o = new Orchestrator();
      o.Start();
      >
      Console.ReadLin e();
      }
      }
      >
      public class Orchestrator
      {
      IData data = new Data();
      IHandlerArgs args = null;
      >
      HandlerBase h1 = new Handler1();
      HandlerBase h2 = new Handler2();
      >
      public void Start()
      {
      args = new HandlerArgs(dat a);
      h1.successor = h2;
      h1.Process(args );
      >
      Console.WriteLi ne(string.Forma t("args.data.fi eld1:
      {0}",args.data. field1));
      Console.WriteLi ne(string.Forma t("data.field 1: {0}",
      data.field1));
      }
      >
      }
      >
      /* Base class for Handlers */
      public abstract class HandlerBase
      {
      public abstract void Process(IHandle rArgs args);
      public HandlerBase successor = null;
      >
      }
      >
      /* Concrete instance of a handler */
      public class Handler1 : HandlerBase
      {
      public override void Process(IHandle rArgs args)
      {
      args.data.field 1 = "Handerl";
      if (successor != null)
      {
      successor.Proce ss(args);
      }
      }
      }
      >
      /* Concrete instance of a handler */
      public class Handler2 : HandlerBase
      {
      public override void Process(IHandle rArgs args)
      {
      args.data = new Data();
      >
      args.data.field 1 = "Hander2";
      if (successor != null)
      {
      successor.Proce ss(args);
      }
      }
      }
      >
      >
      /* Interface for the argument that is passed to the handler */
      public interface IHandlerArgs
      {
      IData data
      {
      get;
      set;
      }
      }
      >
      /* Concrete instance of HandlerArgs */
      public class HandlerArgs : IHandlerArgs
      {
      public HandlerArgs(IDa ta data)
      {
      _data = data;
      }
      >
      IData _data = null;
      public IData data
      {
      get
      {
      return _data;
      }
      set
      {
      _data = value;
      }
      }
      >
      }
      >
      /* Interface for the data packet that will be
      encapsulated in the HandlerArgs.
      */
      public interface IData
      {
      string field1
      {
      get;
      set;
      }
      }
      >
      /* Concrete instance of a data packet */
      public class Data : IData
      {
      private string _field1 = "null";
      public string field1
      {
      get
      {
      return _field1;
      }
      set
      {
      _field1 = value;
      }
      }
      >
      }
      >
      }
      >
      --
      Good luck!
      >
      Shailen Sukul
      Architect
      (BSc MCTS, MCSD.Net MCSD MCAD)
      Ashlen Consulting Service P/L
      (http://www.ashlen.net.au)

      Comment

      • Shailen Sukul

        #4
        RE: Observation on object references

        yes you are right.
        I was *hoping* that the data variable would get updated to "Handler2" but
        what I forgot that o.args.data is a referemce type and re-assigning it does
        not guarantee that whatever is pointing to it will get updated. o.args.data
        just gets re-pointed to a new memory location and data is still pointing at
        the old memory location.

        Wouldn't it be nice if we could declare a reference type in C# that would
        get updated along with its referenced variable.
        so data --args.data
        args.data gets a new reference
        data --new reference

        (sigh)
        --
        Good luck!

        Shailen Sukul
        Architect
        (BSc MCTS, MCSD.Net MCSD MCAD)
        Ashlen Consulting Service P/L
        (http://www.ashlen.net.au)


        "Mark R. Dawson" wrote:
        Hi Shailen,
        if you run your code you get the following output o the command line:
        >
        args.data.field 1: Handler2
        data.field1: Handler1
        >
        To me that seems exactly what I would expect. Basically you Orchestrator
        object o initially creates a Data object data (we will call this d1) and
        passes in the reference to that to the args variable, at this point we know:
        o.args.data.fie ld1 == "null", where data refers to d1 instance.
        >
        You then call processs and pass in the args variable, Handler1 changes the
        field1 value so:
        o.args.data.fie ld1 == "Handler1"
        >
        everything is good so far, the args instance is passed to Handler2, at this
        point Handler2 creates a new instance of the Data object (call this d2) and
        passes that to the args object, so before we had:
        >
        o.args.data -referring to d1 instance we created earlier.
        >
        now we have o.args.data -refers to d2, and set the field value
        o.args.data.fie ld1 == "handler2"
        >
        so back in the following code:
        public void Start()
        {
        args = new HandlerArgs(dat a);
        h1.successor = h2;
        h1.Process(args );
        >
        Console.WriteLi ne(string.Forma t("args.data.fi eld1:
        {0}",args.data. field1));
        Console.WriteLi ne(string.Forma t("data.field 1: {0}", data.field1));
        }
        >
        args.data refers to Data instance d2, so args.data.field 1 == "handler2" and
        your original data field d1 will still have a field value of "Handler1". The
        important point to note is when you did:
        >
        public class Handler2 : HandlerBase
        {
        public override void Process(IHandle rArgs args)
        {
        args.data = new Data();
        >
        you replaced what the args instance referenced. What were you expecting to
        happen?
        >
        Mark.
        --

        >
        >
        "Shailen Sukul" wrote:
        >
        Observed a weird behaviour with object references.
        See code listing below:

        using System;
        using System.Collecti ons.Generic;
        using System.Text;

        namespace PointerExceptio nTest
        {
        /*
        Desc: Apparent weird behaviour of encapsulated reference types
        getting "lost" upon instance renewal.

        The following code example shows a basic implementation of the Chain
        of Responsibility design
        pattern, where the main class (Orchestrator) creates 2 instances of
        the Handler classes, (h1
        and h2) and chains them together. It then passes an event argument
        variable to h1 by calling
        its Process method. The args variable contains a data object that
        will be inspected after the
        chain is complete. It also contains a data variable that is passed as
        a reference to the args constructor.

        Handler1 simply changes a value if args.data.field 1
        Handler2 recreates a new instance of data and assigns it to args.data.

        We would expect Orchestration.a rgs.data to be updated to the new
        reference of data2 in Handler2 and that DOES happen.
        We would also expect Orchestration.d ata to be updated to the new
        reference of data2 in Handler2, but that DOES NOT happen!
        When the args.data gets assigned to a new data variable,
        Orchestration.d ata loses reference to the new value from then on.
        Being a pointer, one would expect it to be updated, but as the output
        clearly shows, it does not.

        */
        class Program
        {
        static void Main(string[] args)
        {
        Orchestrator o = new Orchestrator();
        o.Start();

        Console.ReadLin e();
        }
        }

        public class Orchestrator
        {
        IData data = new Data();
        IHandlerArgs args = null;

        HandlerBase h1 = new Handler1();
        HandlerBase h2 = new Handler2();

        public void Start()
        {
        args = new HandlerArgs(dat a);
        h1.successor = h2;
        h1.Process(args );

        Console.WriteLi ne(string.Forma t("args.data.fi eld1:
        {0}",args.data. field1));
        Console.WriteLi ne(string.Forma t("data.field 1: {0}", data.field1));
        }

        }

        /* Base class for Handlers */
        public abstract class HandlerBase
        {
        public abstract void Process(IHandle rArgs args);
        public HandlerBase successor = null;

        }

        /* Concrete instance of a handler */
        public class Handler1 : HandlerBase
        {
        public override void Process(IHandle rArgs args)
        {
        args.data.field 1 = "Handerl";
        if (successor != null)
        {
        successor.Proce ss(args);
        }
        }
        }

        /* Concrete instance of a handler */
        public class Handler2 : HandlerBase
        {
        public override void Process(IHandle rArgs args)
        {
        args.data = new Data();

        args.data.field 1 = "Hander2";
        if (successor != null)
        {
        successor.Proce ss(args);
        }
        }
        }


        /* Interface for the argument that is passed to the handler */
        public interface IHandlerArgs
        {
        IData data
        {
        get;
        set;
        }
        }

        /* Concrete instance of HandlerArgs */
        public class HandlerArgs : IHandlerArgs
        {
        public HandlerArgs(IDa ta data)
        {
        _data = data;
        }

        IData _data = null;
        public IData data
        {
        get
        {
        return _data;
        }
        set
        {
        _data = value;
        }
        }

        }

        /* Interface for the data packet that will be
        encapsulated in the HandlerArgs.
        */
        public interface IData
        {
        string field1
        {
        get;
        set;
        }
        }

        /* Concrete instance of a data packet */
        public class Data : IData
        {
        private string _field1 = "null";
        public string field1
        {
        get
        {
        return _field1;
        }
        set
        {
        _field1 = value;
        }
        }

        }

        }

        --
        Good luck!

        Shailen Sukul
        Architect
        (BSc MCTS, MCSD.Net MCSD MCAD)
        Ashlen Consulting Service P/L
        (http://www.ashlen.net.au)

        Comment

        • Mark R. Dawson

          #5
          RE: Observation on object references

          I guess if you really want that kind of semantics you can always use pointer
          in C# which will give you that ability, however there are definitely more
          elegant ways around it.

          You could use an event so that when the data in the HandlerArgs is modified
          all items that reference that data can update themselves i.e.

          using System;
          using System.Collecti ons.Generic;
          using System.Text;

          namespace PointerExceptio nTest
          {
          class Program
          {
          static void Main(string[] args)
          {
          Orchestrator o = new Orchestrator();
          o.Start();

          Console.ReadLin e();
          }
          }

          public class Orchestrator
          {
          IData data = new Data();
          HandlerArgs args = null;

          HandlerBase h1 = new Handler1();
          HandlerBase h2 = new Handler2();

          public void Start()
          {
          args = new HandlerArgs(dat a);
          args.DataChange d += new
          HandlerArgs.Dat aChangedEventHa ndler(args_Data Changed);
          h1.successor = h2;
          h1.Process(args );

          Console.WriteLi ne(string.Forma t("args.data.fi eld1:
          {0}",args.data. field1));
          Console.WriteLi ne(string.Forma t("data.field 1: {0}", data.field1));
          }

          void args_DataChange d(IData data)
          {
          this.data = data;
          }

          }

          /* Base class for Handlers */
          public abstract class HandlerBase
          {
          public abstract void Process(Handler Args args);
          public HandlerBase successor = null;

          }

          /* Concrete instance of a handler */
          public class Handler1 : HandlerBase
          {
          public override void Process(Handler Args args)
          {
          args.data.field 1 = "Handerl";
          if (successor != null)
          {
          successor.Proce ss(args);
          }
          }
          }

          /* Concrete instance of a handler */
          public class Handler2 : HandlerBase
          {
          public override void Process(Handler Args args)
          {
          args.data = new Data();

          args.data.field 1 = "Hander2";
          if (successor != null)
          {
          successor.Proce ss(args);
          }
          }
          }

          /* Concrete instance of HandlerArgs */
          public class HandlerArgs
          {
          public HandlerArgs(IDa ta data)
          {
          _data = data;
          }

          IData _data = null;
          public IData data
          {
          get
          {
          return _data;
          }
          set
          {
          _data = value;

          //Let everyone know the data has changed.
          if (DataChanged != null)
          {
          DataChanged(_da ta);
          }
          }
          }

          public delegate void DataChangedEven tHandler(IData data);
          public event DataChangedEven tHandler DataChanged;
          }

          /* Interface for the data packet that will be
          encapsulated in the HandlerArgs.
          */
          public interface IData
          {
          string field1
          {
          get;
          set;
          }
          }

          /* Concrete instance of a data packet */
          public class Data : IData
          {
          private string _field1 = "null";
          public string field1
          {
          get
          {
          return _field1;
          }
          set
          {
          _field1 = value;
          }
          }

          }

          }


          Mark.
          --



          "Shailen Sukul" wrote:
          yes you are right.
          I was *hoping* that the data variable would get updated to "Handler2" but
          what I forgot that o.args.data is a referemce type and re-assigning it does
          not guarantee that whatever is pointing to it will get updated. o.args.data
          just gets re-pointed to a new memory location and data is still pointing at
          the old memory location.
          >
          Wouldn't it be nice if we could declare a reference type in C# that would
          get updated along with its referenced variable.
          so data --args.data
          args.data gets a new reference
          data --new reference
          >
          (sigh)
          --
          Good luck!
          >
          Shailen Sukul
          Architect
          (BSc MCTS, MCSD.Net MCSD MCAD)
          Ashlen Consulting Service P/L
          (http://www.ashlen.net.au)
          >
          >
          "Mark R. Dawson" wrote:
          >
          Hi Shailen,
          if you run your code you get the following output o the command line:

          args.data.field 1: Handler2
          data.field1: Handler1

          To me that seems exactly what I would expect. Basically you Orchestrator
          object o initially creates a Data object data (we will call this d1) and
          passes in the reference to that to the args variable, at this point we know:
          o.args.data.fie ld1 == "null", where data refers to d1 instance.

          You then call processs and pass in the args variable, Handler1 changes the
          field1 value so:
          o.args.data.fie ld1 == "Handler1"

          everything is good so far, the args instance is passed to Handler2, at this
          point Handler2 creates a new instance of the Data object (call this d2) and
          passes that to the args object, so before we had:

          o.args.data -referring to d1 instance we created earlier.

          now we have o.args.data -refers to d2, and set the field value
          o.args.data.fie ld1 == "handler2"

          so back in the following code:
          public void Start()
          {
          args = new HandlerArgs(dat a);
          h1.successor = h2;
          h1.Process(args );

          Console.WriteLi ne(string.Forma t("args.data.fi eld1:
          {0}",args.data. field1));
          Console.WriteLi ne(string.Forma t("data.field 1: {0}", data.field1));
          }

          args.data refers to Data instance d2, so args.data.field 1 == "handler2" and
          your original data field d1 will still have a field value of "Handler1". The
          important point to note is when you did:

          public class Handler2 : HandlerBase
          {
          public override void Process(IHandle rArgs args)
          {
          args.data = new Data();

          you replaced what the args instance referenced. What were you expecting to
          happen?

          Mark.
          --



          "Shailen Sukul" wrote:
          Observed a weird behaviour with object references.
          See code listing below:
          >
          using System;
          using System.Collecti ons.Generic;
          using System.Text;
          >
          namespace PointerExceptio nTest
          {
          /*
          Desc: Apparent weird behaviour of encapsulated reference types
          getting "lost" upon instance renewal.
          >
          The following code example shows a basic implementation of the Chain
          of Responsibility design
          pattern, where the main class (Orchestrator) creates 2 instances of
          the Handler classes, (h1
          and h2) and chains them together. It then passes an event argument
          variable to h1 by calling
          its Process method. The args variable contains a data object that
          will be inspected after the
          chain is complete. It also contains a data variable that is passed as
          a reference to the args constructor.
          >
          Handler1 simply changes a value if args.data.field 1
          Handler2 recreates a new instance of data and assigns it to args.data.
          >
          We would expect Orchestration.a rgs.data to be updated to the new
          reference of data2 in Handler2 and that DOES happen.
          We would also expect Orchestration.d ata to be updated to the new
          reference of data2 in Handler2, but that DOES NOT happen!
          When the args.data gets assigned to a new data variable,
          Orchestration.d ata loses reference to the new value from then on.
          Being a pointer, one would expect it to be updated, but as the output
          clearly shows, it does not.
          >
          */
          class Program
          {
          static void Main(string[] args)
          {
          Orchestrator o = new Orchestrator();
          o.Start();
          >
          Console.ReadLin e();
          }
          }
          >
          public class Orchestrator
          {
          IData data = new Data();
          IHandlerArgs args = null;
          >
          HandlerBase h1 = new Handler1();
          HandlerBase h2 = new Handler2();
          >
          public void Start()
          {
          args = new HandlerArgs(dat a);
          h1.successor = h2;
          h1.Process(args );
          >
          Console.WriteLi ne(string.Forma t("args.data.fi eld1:
          {0}",args.data. field1));
          Console.WriteLi ne(string.Forma t("data.field 1: {0}", data.field1));
          }
          >
          }
          >
          /* Base class for Handlers */
          public abstract class HandlerBase
          {
          public abstract void Process(IHandle rArgs args);
          public HandlerBase successor = null;
          >
          }
          >
          /* Concrete instance of a handler */
          public class Handler1 : HandlerBase
          {
          public override void Process(IHandle rArgs args)
          {
          args.data.field 1 = "Handerl";
          if (successor != null)
          {
          successor.Proce ss(args);
          }
          }
          }
          >
          /* Concrete instance of a handler */
          public class Handler2 : HandlerBase
          {
          public override void Process(IHandle rArgs args)
          {
          args.data = new Data();
          >
          args.data.field 1 = "Hander2";
          if (successor != null)
          {
          successor.Proce ss(args);
          }
          }
          }
          >
          >
          /* Interface for the argument that is passed to the handler */
          public interface IHandlerArgs
          {
          IData data
          {
          get;
          set;
          }
          }
          >
          /* Concrete instance of HandlerArgs */
          public class HandlerArgs : IHandlerArgs
          {
          public HandlerArgs(IDa ta data)
          {
          _data = data;
          }
          >
          IData _data = null;
          public IData data
          {
          get
          {
          return _data;
          }
          set
          {
          _data = value;
          }
          }
          >
          }
          >
          /* Interface for the data packet that will be
          encapsulated in the HandlerArgs.
          */
          public interface IData
          {
          string field1
          {
          get;
          set;
          }
          }
          >
          /* Concrete instance of a data packet */
          public class Data : IData
          {
          private string _field1 = "null";
          public string field1
          {
          get
          {
          return _field1;
          }
          set
          {
          _field1 = value;
          }
          }
          >
          }
          >
          }
          >
          --
          Good luck!
          >
          Shailen Sukul
          Architect
          (BSc MCTS, MCSD.Net MCSD MCAD)
          Ashlen Consulting Service P/L
          (http://www.ashlen.net.au)

          Comment

          • Ciaran O''Donnell

            #6
            RE: Observation on object references

            What you want to do it pass a reference value to a function and if the
            function updates it with a new reference, have it update the caller?
            Simply mark the parameter with the ref keyword in c# which passes a
            reference to the reference to the object so it can be updated. All uses of
            the parameter in the function are exactly the same.

            Ciaran O'Donnell

            "Shailen Sukul" wrote:
            yes you are right.
            I was *hoping* that the data variable would get updated to "Handler2" but
            what I forgot that o.args.data is a referemce type and re-assigning it does
            not guarantee that whatever is pointing to it will get updated. o.args.data
            just gets re-pointed to a new memory location and data is still pointing at
            the old memory location.
            >
            Wouldn't it be nice if we could declare a reference type in C# that would
            get updated along with its referenced variable.
            so data --args.data
            args.data gets a new reference
            data --new reference
            >
            (sigh)
            --
            Good luck!
            >
            Shailen Sukul
            Architect
            (BSc MCTS, MCSD.Net MCSD MCAD)
            Ashlen Consulting Service P/L
            (http://www.ashlen.net.au)
            >
            >
            "Mark R. Dawson" wrote:
            >
            Hi Shailen,
            if you run your code you get the following output o the command line:

            args.data.field 1: Handler2
            data.field1: Handler1

            To me that seems exactly what I would expect. Basically you Orchestrator
            object o initially creates a Data object data (we will call this d1) and
            passes in the reference to that to the args variable, at this point we know:
            o.args.data.fie ld1 == "null", where data refers to d1 instance.

            You then call processs and pass in the args variable, Handler1 changes the
            field1 value so:
            o.args.data.fie ld1 == "Handler1"

            everything is good so far, the args instance is passed to Handler2, at this
            point Handler2 creates a new instance of the Data object (call this d2) and
            passes that to the args object, so before we had:

            o.args.data -referring to d1 instance we created earlier.

            now we have o.args.data -refers to d2, and set the field value
            o.args.data.fie ld1 == "handler2"

            so back in the following code:
            public void Start()
            {
            args = new HandlerArgs(dat a);
            h1.successor = h2;
            h1.Process(args );

            Console.WriteLi ne(string.Forma t("args.data.fi eld1:
            {0}",args.data. field1));
            Console.WriteLi ne(string.Forma t("data.field 1: {0}", data.field1));
            }

            args.data refers to Data instance d2, so args.data.field 1 == "handler2" and
            your original data field d1 will still have a field value of "Handler1". The
            important point to note is when you did:

            public class Handler2 : HandlerBase
            {
            public override void Process(IHandle rArgs args)
            {
            args.data = new Data();

            you replaced what the args instance referenced. What were you expecting to
            happen?

            Mark.
            --



            "Shailen Sukul" wrote:
            Observed a weird behaviour with object references.
            See code listing below:
            >
            using System;
            using System.Collecti ons.Generic;
            using System.Text;
            >
            namespace PointerExceptio nTest
            {
            /*
            Desc: Apparent weird behaviour of encapsulated reference types
            getting "lost" upon instance renewal.
            >
            The following code example shows a basic implementation of the Chain
            of Responsibility design
            pattern, where the main class (Orchestrator) creates 2 instances of
            the Handler classes, (h1
            and h2) and chains them together. It then passes an event argument
            variable to h1 by calling
            its Process method. The args variable contains a data object that
            will be inspected after the
            chain is complete. It also contains a data variable that is passed as
            a reference to the args constructor.
            >
            Handler1 simply changes a value if args.data.field 1
            Handler2 recreates a new instance of data and assigns it to args.data.
            >
            We would expect Orchestration.a rgs.data to be updated to the new
            reference of data2 in Handler2 and that DOES happen.
            We would also expect Orchestration.d ata to be updated to the new
            reference of data2 in Handler2, but that DOES NOT happen!
            When the args.data gets assigned to a new data variable,
            Orchestration.d ata loses reference to the new value from then on.
            Being a pointer, one would expect it to be updated, but as the output
            clearly shows, it does not.
            >
            */
            class Program
            {
            static void Main(string[] args)
            {
            Orchestrator o = new Orchestrator();
            o.Start();
            >
            Console.ReadLin e();
            }
            }
            >
            public class Orchestrator
            {
            IData data = new Data();
            IHandlerArgs args = null;
            >
            HandlerBase h1 = new Handler1();
            HandlerBase h2 = new Handler2();
            >
            public void Start()
            {
            args = new HandlerArgs(dat a);
            h1.successor = h2;
            h1.Process(args );
            >
            Console.WriteLi ne(string.Forma t("args.data.fi eld1:
            {0}",args.data. field1));
            Console.WriteLi ne(string.Forma t("data.field 1: {0}", data.field1));
            }
            >
            }
            >
            /* Base class for Handlers */
            public abstract class HandlerBase
            {
            public abstract void Process(IHandle rArgs args);
            public HandlerBase successor = null;
            >
            }
            >
            /* Concrete instance of a handler */
            public class Handler1 : HandlerBase
            {
            public override void Process(IHandle rArgs args)
            {
            args.data.field 1 = "Handerl";
            if (successor != null)
            {
            successor.Proce ss(args);
            }
            }
            }
            >
            /* Concrete instance of a handler */
            public class Handler2 : HandlerBase
            {
            public override void Process(IHandle rArgs args)
            {
            args.data = new Data();
            >
            args.data.field 1 = "Hander2";
            if (successor != null)
            {
            successor.Proce ss(args);
            }
            }
            }
            >
            >
            /* Interface for the argument that is passed to the handler */
            public interface IHandlerArgs
            {
            IData data
            {
            get;
            set;
            }
            }
            >
            /* Concrete instance of HandlerArgs */
            public class HandlerArgs : IHandlerArgs
            {
            public HandlerArgs(IDa ta data)
            {
            _data = data;
            }
            >
            IData _data = null;
            public IData data
            {
            get
            {
            return _data;
            }
            set
            {
            _data = value;
            }
            }
            >
            }
            >
            /* Interface for the data packet that will be
            encapsulated in the HandlerArgs.
            */
            public interface IData
            {
            string field1
            {
            get;
            set;
            }
            }
            >
            /* Concrete instance of a data packet */
            public class Data : IData
            {
            private string _field1 = "null";
            public string field1
            {
            get
            {
            return _field1;
            }
            set
            {
            _field1 = value;
            }
            }
            >
            }
            >
            }
            >
            --
            Good luck!
            >
            Shailen Sukul
            Architect
            (BSc MCTS, MCSD.Net MCSD MCAD)
            Ashlen Consulting Service P/L
            (http://www.ashlen.net.au)

            Comment

            • Dave Sexton

              #7
              Re: Observation on object references

              Hi Ciaran,

              The "ref" keyword doesn't help in this situation.

              Assigning a new instance to a "ref" argument doesn't change the old reference
              in the original variable, it simply reassigns the reference of the local
              variable:

              class Program
              {
              public object obj;

              public void SetAndReassignF ield(ref object refParam)
              {
              this.obj = refParam;
              this.obj = new object();
              }

              public void SetAndReassignA rg(ref object refParam)
              {
              this.obj = refParam;
              refParam = new object();
              }

              static void Main()
              {
              Program program = new Program();

              object obj = new object();
              program.SetAndR eassignArg(ref obj);

              Console.WriteLi ne(obj == program.obj);

              obj = new object();
              program.SetAndR eassignField(re f obj);

              Console.WriteLi ne(obj == program.obj);

              Console.ReadLin e();
              }
              }

              Output:

              False
              False

              Neither reference of the new instances were retained by the local "obj"
              parameter in the Main method.

              --
              Dave Sexton

              "Ciaran O''Donnell" <CiaranODonnell @discussions.mi crosoft.comwrot e in
              message news:8645E068-4A17-4E83-AC87-5272DFB20ABC@mi crosoft.com...
              What you want to do it pass a reference value to a function and if the
              function updates it with a new reference, have it update the caller?
              Simply mark the parameter with the ref keyword in c# which passes a
              reference to the reference to the object so it can be updated. All uses of
              the parameter in the function are exactly the same.
              >
              Ciaran O'Donnell
              >
              "Shailen Sukul" wrote:
              >
              >yes you are right.
              >I was *hoping* that the data variable would get updated to "Handler2" but
              >what I forgot that o.args.data is a referemce type and re-assigning it does
              >not guarantee that whatever is pointing to it will get updated. o.args.data
              >just gets re-pointed to a new memory location and data is still pointing at
              >the old memory location.
              >>
              >Wouldn't it be nice if we could declare a reference type in C# that would
              >get updated along with its referenced variable.
              >so data --args.data
              >args.data gets a new reference
              >data --new reference
              >>
              >(sigh)
              >--
              >Good luck!
              >>
              >Shailen Sukul
              >Architect
              >(BSc MCTS, MCSD.Net MCSD MCAD)
              >Ashlen Consulting Service P/L
              >(http://www.ashlen.net.au)
              >>
              >>
              >"Mark R. Dawson" wrote:
              >>
              Hi Shailen,
              if you run your code you get the following output o the command line:
              >
              args.data.field 1: Handler2
              data.field1: Handler1
              >
              To me that seems exactly what I would expect. Basically you Orchestrator
              object o initially creates a Data object data (we will call this d1) and
              passes in the reference to that to the args variable, at this point we
              know:
              o.args.data.fie ld1 == "null", where data refers to d1 instance.
              >
              You then call processs and pass in the args variable, Handler1 changes
              the
              field1 value so:
              o.args.data.fie ld1 == "Handler1"
              >
              everything is good so far, the args instance is passed to Handler2, at
              this
              point Handler2 creates a new instance of the Data object (call this d2)
              and
              passes that to the args object, so before we had:
              >
              o.args.data -referring to d1 instance we created earlier.
              >
              now we have o.args.data -refers to d2, and set the field value
              o.args.data.fie ld1 == "handler2"
              >
              so back in the following code:
              public void Start()
              {
              args = new HandlerArgs(dat a);
              h1.successor = h2;
              h1.Process(args );
              >
              Console.WriteLi ne(string.Forma t("args.data.fi eld1:
              {0}",args.data. field1));
              Console.WriteLi ne(string.Forma t("data.field 1: {0}",
              data.field1));
              }
              >
              args.data refers to Data instance d2, so args.data.field 1 == "handler2"
              and
              your original data field d1 will still have a field value of "Handler1".
              The
              important point to note is when you did:
              >
              public class Handler2 : HandlerBase
              {
              public override void Process(IHandle rArgs args)
              {
              args.data = new Data();
              >
              you replaced what the args instance referenced. What were you expecting
              to
              happen?
              >
              Mark.
              --

              >
              >
              "Shailen Sukul" wrote:
              >
              Observed a weird behaviour with object references.
              See code listing below:
              >
              using System;
              using System.Collecti ons.Generic;
              using System.Text;
              >
              namespace PointerExceptio nTest
              {
              /*
              Desc: Apparent weird behaviour of encapsulated reference types
              getting "lost" upon instance renewal.
              >
              The following code example shows a basic implementation of the
              Chain
              of Responsibility design
              pattern, where the main class (Orchestrator) creates 2 instances
              of
              the Handler classes, (h1
              and h2) and chains them together. It then passes an event
              argument
              variable to h1 by calling
              its Process method. The args variable contains a data object
              that
              will be inspected after the
              chain is complete. It also contains a data variable that is
              passed as
              a reference to the args constructor.
              >
              Handler1 simply changes a value if args.data.field 1
              Handler2 recreates a new instance of data and assigns it to
              args.data.
              >
              We would expect Orchestration.a rgs.data to be updated to the new
              reference of data2 in Handler2 and that DOES happen.
              We would also expect Orchestration.d ata to be updated to the new
              reference of data2 in Handler2, but that DOES NOT happen!
              When the args.data gets assigned to a new data variable,
              Orchestration.d ata loses reference to the new value from then on.
              Being a pointer, one would expect it to be updated, but as the
              output
              clearly shows, it does not.
              >
              */
              class Program
              {
              static void Main(string[] args)
              {
              Orchestrator o = new Orchestrator();
              o.Start();
              >
              Console.ReadLin e();
              }
              }
              >
              public class Orchestrator
              {
              IData data = new Data();
              IHandlerArgs args = null;
              >
              HandlerBase h1 = new Handler1();
              HandlerBase h2 = new Handler2();
              >
              public void Start()
              {
              args = new HandlerArgs(dat a);
              h1.successor = h2;
              h1.Process(args );
              >
              Console.WriteLi ne(string.Forma t("args.data.fi eld1:
              {0}",args.data. field1));
              Console.WriteLi ne(string.Forma t("data.field 1: {0}",
              data.field1));
              }
              >
              }
              >
              /* Base class for Handlers */
              public abstract class HandlerBase
              {
              public abstract void Process(IHandle rArgs args);
              public HandlerBase successor = null;
              >
              }
              >
              /* Concrete instance of a handler */
              public class Handler1 : HandlerBase
              {
              public override void Process(IHandle rArgs args)
              {
              args.data.field 1 = "Handerl";
              if (successor != null)
              {
              successor.Proce ss(args);
              }
              }
              }
              >
              /* Concrete instance of a handler */
              public class Handler2 : HandlerBase
              {
              public override void Process(IHandle rArgs args)
              {
              args.data = new Data();
              >
              args.data.field 1 = "Hander2";
              if (successor != null)
              {
              successor.Proce ss(args);
              }
              }
              }
              >
              >
              /* Interface for the argument that is passed to the handler */
              public interface IHandlerArgs
              {
              IData data
              {
              get;
              set;
              }
              }
              >
              /* Concrete instance of HandlerArgs */
              public class HandlerArgs : IHandlerArgs
              {
              public HandlerArgs(IDa ta data)
              {
              _data = data;
              }
              >
              IData _data = null;
              public IData data
              {
              get
              {
              return _data;
              }
              set
              {
              _data = value;
              }
              }
              >
              }
              >
              /* Interface for the data packet that will be
              encapsulated in the HandlerArgs.
              */
              public interface IData
              {
              string field1
              {
              get;
              set;
              }
              }
              >
              /* Concrete instance of a data packet */
              public class Data : IData
              {
              private string _field1 = "null";
              public string field1
              {
              get
              {
              return _field1;
              }
              set
              {
              _field1 = value;
              }
              }
              >
              }
              >
              }
              >
              --
              Good luck!
              >
              Shailen Sukul
              Architect
              (BSc MCTS, MCSD.Net MCSD MCAD)
              Ashlen Consulting Service P/L
              (http://www.ashlen.net.au)

              Comment

              Working...