Friday, April 25, 2014

Handing an instance of a class into a method signature is much like handing a value type into a method signature as a ref type variable.

If you use the instance variable procedurally after you hand it into the method, you will need to keep in mind that the instance may have changed (inside the method). A new copy on the heap with a new pointer is not created when the reference type is handed into a method signature. You are still using the same pointer referencing the same spot on the heap. Observe this in the following four items:

  1. using System;
    namespace ReferenceTypeExperiment.Stuff
    {
       public class MyState
       {
          public string MyString { get; set; }
          public bool MyBool { get; set; }
          public int MyInt { get; set; }
          public DateTime MyDateTime { get; set; }
       }
    }
     
  2. using System;
    namespace ReferenceTypeExperiment.Stuff
    {
       public class MyStateChanger : IMyStateChanger
       {
          public void MaybeChangeState(MyState myState)
          {
             myState.MyString = "Whatever";
             myState.MyBool = true;
             myState.MyInt = 13;
             myState.MyDateTime = new DateTime(1974, 8, 24);
          }
       }
    }
     
  3. namespace ReferenceTypeExperiment.Stuff
    {
       public interface IMyStateChanger
       {
          void MaybeChangeState(MyState myState);
       }
    }
     
  4. using System;
    namespace ReferenceTypeExperiment.Stuff
    {
       public class MyProcedure
       {
          public void Act()
          {
             MyState myState = new MyState();
             myState.MyString = "SomethingErOther";
             myState.MyBool = false;
             myState.MyInt = 42;
             myState.MyDateTime = new DateTime(2007, 2, 23);
             
             IMyStateChanger myStateChanger = new MyStateChanger();
             myStateChanger.MaybeChangeState(myState);
             
             var breakpoint = "stop here and look at myState";
          }
       }
    }
     

If you run the code in the Act method and hit a breakpoint at its last line, you may observe how myState has changed:

Also note that if you create another reference variable from an existing pointer like so...

MyState myOtherState = myState;

 
 

...that it will "have in it" exactly what the reference variable it was made from "has in it" as both variables don't really have contents but instead point to the same blob of storage on the heap. Once the two variables are tied together like this, it may be tricky to break them apart. This is one way to go about it:

using (var stream = new MemoryStream())
{
   var formatter = new BinaryFormatter();
   formatter.Serialize(stream, myOtherState);
   stream.Position = 0;
   myOtherState = (MyState)formatter.Deserialize(stream);
}

 
 

For this to work with the code I have above, the code above needs a stitch. The MyState class needs to be decorated with the Serializable attribute like so:

[Serializable()]

 
 

It now occurs to me that in this visitor pattern example I don't need to do this:

lucifer = siteOfThermonuclearExplosion.WelcomeVisitor(lucifer);
gregor = siteOfThermonuclearExplosion.WelcomeVisitor(gregor);
felix = siteOfThermonuclearExplosion.WelcomeVisitor(felix);
spot = siteOfThermonuclearExplosion.WelcomeVisitor(spot);

 
 

...as I all I really need is:

siteOfThermonuclearExplosion.WelcomeVisitor(lucifer);
siteOfThermonuclearExplosion.WelcomeVisitor(gregor);
siteOfThermonuclearExplosion.WelcomeVisitor(felix);
siteOfThermonuclearExplosion.WelcomeVisitor(spot);

 
 

...I could just change WelcomeVisitor to not hand back the same pointer handed in.

No comments:

Post a Comment