Replacing an object on so that all references to it are kept - c#

I wanted to understand more about how the ref keyword works so I made the following experiment:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var main = new Main { Property = 1 };
var dependent = new Dependent(main);
void ChangeRef(ref Main Oldmain, Main newMain)
{
Oldmain = newMain;
}
ChangeRef(ref main, new Main { Property = 5 });
Assert.AreEqual(5,dependent.Main.Property);
}
}
public class Main
{
public int Property { get; set; }
}
public class Dependent
{
public Dependent(Main main)
{
Main = main;
}
public Main Main { get; set; }
}
As you can see I was expecting to be able to replace the object that main was referencing while keeping the reference, but the test fails and with the value still being 1. Could someone elaborate abit on why that wasnt working or point me to a place where I can read more?
Update:
Like someone answered below, but later removed.
Why doesnt it work if I pass the main object by reference to the dependent in the constructor?
Shouldnt they all have the same reference?

As others have pointed, you cannot instantly make all variables and fields in your program point to a different instance.
But if you want to reflect a change in all parts of the program, the simplest way is to wrap it in a different class (like your Dependent class). Then you can share the class with other parts of the program, and change its properties instead:
class SomeOtherObject
{
readonly Dependent _dependent;
public Dependent { get { return _dependent; }}
public SomeOtherObject(Dependent dependent)
{
_dependent = dependent;
}
public void Print()
{
Console.WriteLine(_dependent.Main.Property);
}
}
So now you can do this:
var dependent = new Dependent(new Main { Property = 1 });
var someOtherObject = new SomeOtherObject(dependent);
// this will print "1"
someOtherObject.Print();
dependent.Main = new Main { Property = 5; };
// this will print "5"
someOtherObject.Print();
In this case, obviously, simply changing dependent.Main.Property would also do the trick. So, if all parts of your program point to a single object, you can mutate it (i.e. change its internal data) and everyone will see the change, but you cannot make all parts of your program change what they are pointing to.
It's worth noting that you need to be careful when doing this in multithreaded programs; you rarely want some other thread to be able to randomly change your internal data.
That's also why it's best to try to keep your properties readonly, and your objects immutable, if possible.

After doing that
var main = new Main { Property = 1 };
You have object of type Main allocated somewhere in memory (let's name it Main1), at some memory address X, and variable main points to that object. "Points" means it literally stores address of that object Main1, so main contains X.
Then you pass reference to Main1 to the constructor of Dependent object
var dependent = new Dependent(main);
Dependent object is also allocated somewhere in memory, and one of its fields stores reference to Main1 object. So dependent.Main also stores X.
When you do
ChangeRef(ref main, new Main { Property = 5 });
You allocate new object Main5 somewhere at memory address Y. Now you change what address variable main points to. Before it stored address X (address of Main1), now it stores address Y (address of Main5). But dependent.Main still stores address X, because you didn't change it in any way, so it still points to object Main1.

Related

Constantly repeating class constructor - Class object seems to be instantiating itself

I'm currently running into an issue where a class is seemingly creating an instance of itself or repeating its constructor causing an infinite loop.
I got a Singleton called Hotel. This currently holds a 2d array of Room (Room[,] HotelGridLayout) and a list of its guests/customers (Guests). It creates a Customer at the last line of the constructor in Hotel.
Customer Constructor:
public Customer(string _classPref, Room _entrance)
{
ClassificationPreference = _classPref; // Preference for classification (type of room)
FilePath = #"HotelPictures\Customer.png"; // String for image file path
CheckIn(_entrance);
}
CheckIn():
private void CheckIn(Room _entrance)
{
_entrance.UpdateForgroundImage(FilePath);
Room _roomAvailable = HotelForm.Instance.CheckInAtHotel(ClassificationPreference);
// some more stuff that doesn't even get reached.
}
important parts of Hotel constructor:
private HotelForm()
{
HotelGridLayout = JsonToHotelLayout.DeserializeHotelLayout(JsonString); // not the issue
Customers = new List<MovableObject>();
Cleaners = new List<MovableObject>();
MovableObject _testGuest = new Customer("5 stars", HotelGridLayout[1, 0]);
}
Here is the deal: After _entrance.UpdateForgroundImage(FilePath);, right before calling CheckInAtHotel(), it either creates a new Customer class or re-runs the constructor. It should not have anything to do with UpdateForgroundImage since this literately only assigns the Image displayed in a PictureBox (it's 1 line).
UpdateForgroundImage:
public virtual void UpdateForgroundImage(string _imageFilePath)
{
PictureBoxRoom.Image = Image.FromFile(Path.Combine("..", "..", "..", "..", _imageFilePath));
}
What is going on? Am I just blind and missing something completely obvious?
Source of issue:
I found the source of the issue: In Customer.CheckIn() I was getting the HotelForm instance via it's static property. Since that Customer was created in the constructor of HotelForm and requested the instance before it was set, it kept creating/updating instances of HotelForm; this code shows why:
public static HotelForm Instance
{
get
{
lock (_padlock)
{
if (_instance == null)
{
_instance = new HotelForm();
}
return _instance;
}
}
}
The instance didn't exist yet since the constructor wasn't finished (so to say), so it created a new instance of HotelForm, repeating the process thus constantly creating Customers.

Trouble understanding reference types / reference copying in Service Locator implementation

In implementing a Service Locator, I've come across something I'm confused about with regards to reference types.
In the code below, I have a static class ServiceLocator which exposes 2 static methods, GetService and ProvideService - get returns the current service, and provide takes a new service as an argument and assigns it to the current service variable. If the provided service is null, it assigns currentService to a static defaultService initialised at the start of the class declaration. Simple stuff:
public static class ServiceLocator {
private static readonly Service defaultService = new Service();
private static Service currentService = defaultService;
public static Service GetService() {
return currentService;
}
public static void ProvideService(Service service) {
currentService = service ?? defaultService;
}
}
What i'm confused about is this: I have a separate class which stores a reference to the currentService at the start of its class declaration in the variable named referenceToCurrentServiceAtStart. When I provide the service locator with a new Service instance to update the current service, referenceToCurrentServiceAtStart appears instead to maintain the reference to defaultService:
public class ClassThatUsesService {
private Service referenceToCurrentServiceAtStart = ServiceLocator.GetService();
private static ClassThatUsesService() {
ServiceLocator.ProvideService(new Service());
// this variable appears to still reference the defaultService
referenceToCurrentServiceAtStart != ServiceLocator.GetService()
}
}
So the references appear to follow this kind of chain:
referenceToCurrentServiceAtStart -> defaultService -> (Service in memory)
Which is understandable, since referenceToCurrentServiceAtStart simply copies the currentService reference. However, the behaviour I'm looking for/would like is for referenceToCurrentServiceAtStart to always reference whatever currentService references, so it's updated by Provide(). Something more akin to:
referenceToCurrentServiceAtStart -> currentService -> (Service> in memory)
So, is this behaviour possible? I'm really unsure of how I'd achieve this kind of reference behaviour. I'm new to C# so it's very possible there's some obvious language feature I'm clueless about. Any help would be greatly appreciated.
is this behaviour possible?
No, not as you've described it. As you're already aware, all you get is a copy of the original reference. Changing the original reference doesn't change the copy, any more than copying the value of an int variable to another would allow you to later change the original and have the copy change:
int original = 17;
int copy = original;
original = 19;
// "copy" is still 17, of course!
If you want to always have the current value of the reference in ServiceLocator, then you should just always retrieve the value from that class, rather than using a local field. In your above example, you might indirect through a property, e.g.:
public class ClassThatUsesService {
private Service referenceToCurrentServiceAtStart => ServiceLocator.GetService();
}
It's a one character change (the = becomes =>), but don't be fooled. It's a significant change in implementation. What you wind up with instead of a field, is a read-only property (i.e. has only a get method and no set method), where that property's get method calls the ServiceLocator.GetService() method and returns the result.
Personally, I wouldn't bother. Unless you have some very strong expectation that the implementation of referenceToCurrentServiceAtStart will change in the future, you should just call ServiceLocator.GetService() directly. Don't even have the referenceToCurrentServiceAtStart property. Since the code expects to always get the current value, the best way to ensure that is to just always get the current value, straight from the class where that value is stored.
Finally, I'll take the opportunity to show a scenario that is similar to what you're asking, but not exactly. In particular, because you're trying to store the reference in a class field, the above is how you need to do it. But, the latest C# has "reference return values", which must be stored in "ref locals". Since you want to reference a static field, which is guaranteed to always exist, you can in fact return a reference to the field, store that in a local, and when you retrieve the local variable's value, it will always have whatever is in the field, because it's a reference to the field, not a copy of it.
You can see the example in the documentation (see links above), but here's another example that is more similar to what you're doing:
class Program
{
static void Main(string[] args)
{
// stores a reference to the value returned by M1(), which is to say,
// a reference to the B._o field.
ref A a1 = ref B.M1();
// Keep the original value, and create a new A instance
A original = a1, a2 = new A();
// Update the B._o field to the new A instance
B.M2(a2);
// Check the current state
Console.WriteLine($"original.ID: {original.ID}");
Console.WriteLine($"a1.ID: {a1.ID}");
Console.WriteLine($"a2.ID: {a2.ID}");
}
}
class A
{
private static int _id;
public int ID { get; }
public A()
{
ID = ++_id;
}
}
class B
{
private static A _o = new A();
public static ref A M1()
{
// returns a _reference_ to the _o field, rather than a copy of its value
return ref _o;
}
public static void M2(A o)
{
_o = o;
}
}
When you run the above, you'll get this output:
original.ID: 1
a1.ID: 2
a2.ID: 2
In other words, the variable a1 winds up yielding the same value found in a2, which is the new object passed to the B.M2() method to modify the B._o field, while the original copy of the B._o field value remains a reference to the original object that field referenced.
This doesn't work in your case, because the ref value that's returned has to be stored in a ref local. You can't put it into a class field. But it's similar enough to your scenario that I wanted to mention it, in case you want to change your design to allow that, or want to use that technique in some other scenario that does work in that way.

C# copying instance and modify new, while the previous is unchanged

I have such a problem with creating a new instances of object based on the existing instances.
To explain better what I want to do - imagine
I have one instance which I am passing to function and I want to copy the content of this existing instance to another instance and add something more to this new instance (but what is important that the first instance is not going to be changed at all).
So the first instance is 'freezed' and the new one is cloned and modified.
What I have done is the model:
public class Scenario
{
public List<SingleMove> listOfMoves { get; set; }
public List<int> listOfScores { get; set; }
public int totalScore { get; set; }
public Scenario(Scenario currentScenario)
{
listOfMoves = currentScenario.listOfMoves;
listOfScores = currentScenario.listOfScores;
totalScore = currentScenario.totalScore;
}
...
and now the function which is working on such objects
static public void ActivitiesForMoveWhites(Scenario currentScenario, int positionTo, int positionFrom)
{
Scenario singleScenario = new Scenario(currentScenario);
SingleMove singleMove = new SingleMove();
singleMove.checker = Configuration.chessfield[positionFrom];
singleMove.positionFrom = Logic.TranslateNumberOfFieldToString(positionFrom); //tutaj moze przechowywac to jako position czyli w intcie ?
singleMove.positionTo = Logic.TranslateNumberOfFieldToString(positionTo); //tutaj moze przechowywac to jako position czyli w intcie ?
singleScenario.listOfMoves.Add(singleMove);
Configuration.listOfScenarios.Add(singleScenario);
//Configuration.listOfScenarios.Add(new Scenario(singleScenario));
...
The problem is that all the different scenarios have the same listOfMoves. But it should be not like this. The current scenario should have copy the previous scenario content, add something, and the scenario that the content was copied from should stay at it was. So finally I have scenarios with different listOfMoves, not same !
Sorry for bit chaotic explanation. Please ask if you have questions or you need more details.
EDIT:
There was one more question but I solved it.
You must also clone the lists in your Scenario because = doesn't create a new instance of the list.
Try something like this in your constructor:
listOfMoves = new List<SingleMove>(currentScenario.listOfMoves.Select(x => (SingleMove)x.Clone()));
Edit:
You must implement IClonable in your SingleMove Object

c# doesn't work struct adding to list

I have
struct Park
{
...
}
and I need, to add 10 elements of this struct to List.
public List<Park> Parks = new List<Park>();
static void Main(string[] args)
{
new Program().CreatePark();
...
}
public void CreatePark()
{
for (int i = 1; i <= 10; i++)
{
Park temp = new Park();
temp.ID = i;
temp.Number = "abc";
temp.Property = false;
try
{
Parks.Add(temp);
}
catch (ArgumentException)
{
Console.WriteLine("oh no :(");
}
Console.WriteLine(temp.ToString());
}
}
And this doesn't work. Console.WriteLine(temp.ToString()); - this shows struct element, but when I want to print all elements, from Parks it's not working.
Parks.Count shows 0.
Given that you're using the statement: new Program().CreatePark(); the list of objects that you've created is not accessible after you create them. You have already thrown away the instance of the object holding the List. You have no way to access the list that you created to know what you had put in it.
The only thing the rest of your code could possibly do is create an entirely separate instance that has its own list, one that is entirely different from the one that you populated.
You'll need to hold onto the reference to the Program object if you want to inspect the list after you've populated it.
Parks is an instance variable.
CreateParks is an instance method.
Main is a static method, so you can't access the instance method or variable from it.
new Program() creates a new instance of the Program class.
Since the newly created program is not stored in a variable, it is discarded, and you can't access its Parks list after that.
To access the Parks list, you need to keep a reference to the instance of the program class like this:
Program program = new Program();
program .CreateParks();
foreach(Park park in program .Parks)
{
// Do something with the park
}
For anything more than a simple program, you will be better off having all the logic which isn't related to starting the program outside the Program class.
For example, you could create a new ParkManager class which is responsible for creating the parks and providing access to them.
Your program would look a bit like this:
public static class Program
{
static void main(string[] args)
{
ParkManager parkManager = new ParkManager();
parkManager.CreateParks();
foreach (Park park in parkManager.Parks)
{
// do something with park
}
}
}
public class ParkManager
{
// Setter is private to prevent other classes from replacing the list
// This doesn't prevent other classes from adding or removing,
// replacing or editing items in the list
public List<Park> Parks {get; private set;}
// Constructor - you could populate the list from here if appropriate
public void ParkManager()
{
Parks = new List<Park>();
}
public void CreateParks()
{
// Populate the list of parks here
}
}

C# Private member shared by all class instances

I am currently working with C# using the Unity3D engine and have come upon the following problem:
I created a class that has two private references to instances of another class which it has to access. Once I create multiple instances of the class and set the references I found out that all instances were using the same variable. I realized this as I was destroying an instance and just before that set the two variables holding the references to null. Immediately after doing that all other instances were throwing NullReferenceExceptions because they were still trying to access the references. The referenced objects are fine, other scripts can still access them.
Here is some pseudo code illustrating the structure:
public class Character
{
// Character data
}
public class StatusEffect
{
private Character target;
private Character originator;
public void Init(Character _Target, Character _Originator)
{
target = _Target;
originator = _Originator;
}
public void Destroy()
{
target = null;
originator = null;
}
}
In the program it would be called like this:
StatusEffect effect = new StatusEffect();
effect.Init(player1, player2);
// Time goes by
effect.Destroy();
After calling Destroy() every StatusEffect's two references will be null.
This is not only an issue when destroying StatusEffects, but also when creating new ones. As soon as I touch the references from within a new instance all StatusEffects will reference the two Characters specified by the new StatusEffect.
I do not understand why or how I can fix this issue. Can someone enlighten me on this matter?
Cheers,
Valtaroth
EDIT:
Here is the real code as requested:
I have a container class holding several StatusEffects. As soon as it starts, it initializes all of them.
public class CElementTag
{
// ..Other data..
public float f_Duration; // Set in the editor
private CGladiator gl_target;
private CGladiator gl_originator;
private float f_currentDuration;
public CStatusEffect[] ar_statusEffects;
// Starts the effect of the element tag
public void StartEffect(CGladiator _Originator, CGladiator _Target)
{
gl_originator = _Originator;
gl_target = _Target;
f_currentDuration = f_Duration;
for(int i = 0; i < ar_statusEffects.Length; i++)
ar_statusEffects[i].Initialize(gl_originator, gl_target);
}
// Ends the effect of the element tag
public void EndEffect()
{
for(int i = 0; i < ar_statusEffects.Length; i++)
{
if(ar_statusEffects[i] != null)
ar_statusEffects[i].Destroy();
}
}
// Called every update, returns true if the tag can be destroyed
public bool ActivateEffect()
{
f_currentDuration -= Time.deltaTime;
if(f_currentDuration <= 0.0f)
{
EndEffect();
return true;
}
for(int i = 0; i < ar_statusEffects.Length; i++)
{
if(ar_statusEffects[i] != null && ar_statusEffects[i].Update())
RemoveStatusEffect(i);
}
return false;
}
// Removes expired status effects
private void RemoveStatusEffect(int _Index)
{
// Call destroy method
ar_statusEffects[_Index].Destroy();
// Remove effect from array
for(int i = _Index; i < ar_statusEffects.Length - 1; i++)
ar_statusEffects[i] = ar_statusEffects[i+1];
ar_statusEffects[ar_statusEffects.Length - 1] = null;
}
}
The actual StatusEffect class is holding the two references as well as some other data it needs to work. It has virtual methods because there are some classes inheriting from it.
public class CStatusEffect
{
// ..Necessary data..
// References
protected CGladiator gl_target;
protected CGladiator gl_originator;
virtual public void Initialize(CGladiator _Target, CGladiator _Originator)
{
gl_target = _Target;
gl_originator = _Originator;
// ..Initialize other necessary stuff..
}
virtual public void Destroy()
{
gl_target = null;
gl_originator = null;
// ..Tidy up other data..
}
virtual public bool Update()
{
// ..Modifying data of gl_target and gl_originator..
// Returns true as soon as the effect is supposed to end.
}
}
That should be all the relevant code concerning this problem.
EDIT2
#KeithPayne I have a static array of ElementTags defined in the editor and saved to xml. At the beginning of the program the static array is loading the xml and stores all element tags. When creating a new element tag to use I utilize this constructor:
// Receives a static tag as parameter
public CElementTag(CElementTag _Tag)
{
i_ID = _Tag.i_ID;
str_Name = _Tag.str_Name;
enum_Type = _Tag.enum_Type;
f_Duration = _Tag.f_Duration;
ar_statusEffects = new CStatusEffect[_Tag.ar_statusEffects.Length];
Array.Copy(_Tag.ar_statusEffects, ar_statusEffects, _Tag.ar_statusEffects.Length);
}
Do I have to use a different method to copy the array to the new tag? I thought Array.Copy would make a deep copy of the source array and stored it in the destination array. If it is in fact making a shallow copy, I understand where the problem is coming from now.
From Array.Copy Method (Array, Array, Int32):
If sourceArray and destinationArray are both reference-type arrays or
are both arrays of type Object, a shallow copy is performed. A shallow
copy of an Array is a new Array containing references to the same
elements as the original Array. The elements themselves or anything
referenced by the elements are not copied. In contrast, a deep copy of
an Array copies the elements and everything directly or indirectly
referenced by the elements.
Consider this fluent version of the StatusEffect class and its usage below:
public class StatusEffect
{
public Character Target { get; private set; }
public Character Originator { get; private set; }
public StatusEffect Init(Character target, Character originator)
{
Target = target.Clone()
Originator = originator.Clone();
return this;
}
//...
}
public CElementTag(CElementTag _Tag)
{
i_ID = _Tag.i_ID;
str_Name = _Tag.str_Name;
enum_Type = _Tag.enum_Type;
f_Duration = _Tag.f_Duration;
ar_statusEffects = _Tag.ar_statusEffects.Select(eff =>
new StatusEffect().Init(eff.Target, eff.Originator)).ToArray();
// ar_statusEffects = new CStatusEffect[_Tag.ar_statusEffects.Length];
// Array.Copy(_Tag.ar_statusEffects, ar_statusEffects, _Tag.ar_statusEffects.Length);
}
Because you're passing in references to the objects via your Init() method, you're not actually "copying" the objects, just maintaining a reference to the same underlying objects in memory.
If you have multiple players with the same references to the same underlying objects, then changes made by player 1 will effect the objects being used by player 2.
Having said all that, you're not actually disposing the objects in your Destory method. Just setting the local instance references to Null which shouldn't affect any other instances of StatusEffects. Are you sure something else isn't disposing the objects, or that you haven't properly init'd your other instances.
If you do want to take a full copy of the passed in objects, take a look at the ICloneable interface. It looks like you want to pass in a copy of the objects into each Player.
public class Character : ICloneable
{
// Character data
//Implement Clone Method
}
public class StatusEffect
{
private Character target;
private Character originator;
public void Init(Character _Target, Character _Originator)
{
target = _Target.Clone()
originator = _Originator.Clone();
}
The fields aren't shared(static) among other instances. So calling target = null; in Destroy() won't affect other instances.
StatusEffect effect1 = new StatusEffect();
effect1.Init(player1, player2);
StatusEffect effect2 = new StatusEffect();
effect2.Init(player1, player2);
// Time goes by
effect2.Destroy();
// Some more time goes by
// accessing effect1.target won't give a `NullReferenceException` here unless player1 was null before passed to the init.
effect1.Destroy();
I think you did forget the Init(..) on the other instances. Every time you create an instance of StatusEffect, you need to call Init(...).
Update:
This line will clear the reference to the effect, but you never recreate it:
ar_statusEffects[ar_statusEffects.Length - 1] = null;
so the next time you call ar_statusEffects[x].Update() or Initialize() etc it will throw a NullReferenceException
If you want to clear out effects within you array, you could create an Enable bool in the effect, this way you only have to set/reset it.
for(int i = 0; i < ar_statusEffects.Length; i++)
if(ar_statusEffects[i].IsEnabled)
ar_statusEffects[i].Update();
Why don't you use a List instead? Arrays will be faster as long you don't have to shuffle in it. (like circulair buffers etc)
Thanks to Keith Payne I figured out where the problem was. I was creating a deep copy of CElementTag, but not of my ar_statusEffects array. I wrongly assumed Array.Copy was creating a deep copy of an array when it actually was not.
I implemented the IClonable interface for my CStatusEffect and use the Clone() method to create a true deep copy for each member of the static array and add it to the new tags ar_statusEffects array. This way I have seperate instances of the effects instead of references to the same static effect.
Thanks to everyone, especially Keith Payne, for their help and support!

Categories