Observation on object references

G

Guest

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)
 
G

Guest

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

args.data.field1: 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.field1 == "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.field1 == "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.field1 == "handler2"

so back in the following code:
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));
}

args.data refers to Data instance d2, so args.data.field1 == "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(IHandlerArgs args)
{
args.data = new Data();

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

Mark.
--
http://www.markdawson.org


Shailen Sukul said:
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)
 
D

Dave Sexton

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(data);

args.data.field1 = "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.field1 = "Handler 2";

Console.WriteLine("Encapsulated value: " + args.data.field1);
// Prints "Handler 2"

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

--
Dave Sexton

Shailen Sukul said:
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)
 
G

Guest

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 said:
Hi Shailen,
if you run your code you get the following output o the command line:

args.data.field1: 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.field1 == "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.field1 == "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.field1 == "handler2"

so back in the following code:
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));
}

args.data refers to Data instance d2, so args.data.field1 == "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(IHandlerArgs args)
{
args.data = new Data();

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

Mark.
--
http://www.markdawson.org


Shailen Sukul said:
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)
 
G

Guest

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.Collections.Generic;
using System.Text;

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

Console.ReadLine();
}
}

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

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

public void Start()
{
args = new HandlerArgs(data);
args.DataChanged += new
HandlerArgs.DataChangedEventHandler(args_DataChanged);
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));
}

void args_DataChanged(IData data)
{
this.data = data;
}

}

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

}

/* Concrete instance of a handler */
public class Handler1 : HandlerBase
{
public override void Process(HandlerArgs args)
{
args.data.field1 = "Handerl";
if (successor != null)
{
successor.Process(args);
}
}
}

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

args.data.field1 = "Hander2";
if (successor != null)
{
successor.Process(args);
}
}
}

/* Concrete instance of HandlerArgs */
public class HandlerArgs
{
public HandlerArgs(IData 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(_data);
}
}
}

public delegate void DataChangedEventHandler(IData data);
public event DataChangedEventHandler 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.
--
http://www.markdawson.org


Shailen Sukul said:
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 said:
Hi Shailen,
if you run your code you get the following output o the command line:

args.data.field1: 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.field1 == "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.field1 == "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.field1 == "handler2"

so back in the following code:
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));
}

args.data refers to Data instance d2, so args.data.field1 == "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(IHandlerArgs args)
{
args.data = new Data();

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

Mark.
--
http://www.markdawson.org


Shailen Sukul said:
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)
 
G

Guest

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 said:
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 said:
Hi Shailen,
if you run your code you get the following output o the command line:

args.data.field1: 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.field1 == "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.field1 == "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.field1 == "handler2"

so back in the following code:
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));
}

args.data refers to Data instance d2, so args.data.field1 == "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(IHandlerArgs args)
{
args.data = new Data();

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

Mark.
--
http://www.markdawson.org


Shailen Sukul said:
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)
 
D

Dave Sexton

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 SetAndReassignField(ref object refParam)
{
this.obj = refParam;
this.obj = new object();
}

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

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

object obj = new object();
program.SetAndReassignArg(ref obj);

Console.WriteLine(obj == program.obj);

obj = new object();
program.SetAndReassignField(ref obj);

Console.WriteLine(obj == program.obj);

Console.ReadLine();
}
}

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 said:
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 said:
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 said:
Hi Shailen,
if you run your code you get the following output o the command line:

args.data.field1: 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.field1 == "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.field1 == "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.field1 == "handler2"

so back in the following code:
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));
}

args.data refers to Data instance d2, so args.data.field1 == "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(IHandlerArgs args)
{
args.data = new Data();

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

Mark.
--
http://www.markdawson.org


:

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)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top