Observed a weird behaviour with object references.
See code listing below:
using System;
using System.Collections.Generic;
using System.Text;
namespace PointerExceptionTest
{
/*
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.field1
Handler2 recreates a new instance of data and assigns it to args.data.
We would expect Orchestration.args.data to be updated to the new
reference of data2 in Handler2 and that DOES happen.
We would also expect Orchestration.data 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.data 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.ReadLine();
}
}
public class Orchestrator
{
IData data = new Data();
IHandlerArgs args = null;
HandlerBase h1 = new Handler1();
HandlerBase h2 = new Handler2();
public void Start()
{
args = new HandlerArgs(data);
h1.successor = h2;
h1.Process(args);
Console.WriteLine(string.Format("args.data.field1:
{0}",args.data.field1));
Console.WriteLine(string.Format("data.field1: {0}", data.field1));
}
}
/* Base class for Handlers */
public abstract class HandlerBase
{
public abstract void Process(IHandlerArgs args);
public HandlerBase successor = null;
}
/* Concrete instance of a handler */
public class Handler1 : HandlerBase
{
public override void Process(IHandlerArgs args)
{
args.data.field1 = "Handerl";
if (successor != null)
{
successor.Process(args);
}
}
}
/* Concrete instance of a handler */
public class Handler2 : HandlerBase
{
public override void Process(IHandlerArgs args)
{
args.data = new Data();
args.data.field1 = "Hander2";
if (successor != null)
{
successor.Process(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(IData 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)