Using the NavigationParameters collection within Prism, we are passing an object from one ContentPage to another ContentPage which displays as a modal.
The modal allows a user to edit the data. If the user decides to cancel the edit form, we call:
NavigationService.GoBackAsync(null, true).
Once navigated back to the previous page, the original property that was passed through to the modal has updated with the edited values without setting it.
Are NavigationParameters passed as a reference within NavigateAsync? What’s the best way of preventing this from happening?
Using the NavigationParameters collection within Prism, we are passing an object [...] [Emphasis mine]
You are setting an object in the NavigationParameters. Instances of classes (objects) are passed by reference in C#, instances of structures are passed by value. For structures there are semantics to copy and compare values (i.e. all public properties are copied and compared respectively), but for classes there are no similar semantics.
Please see the documentation:
Because classes are reference types, a variable of a class object holds a reference to the address of the object on the managed heap. If a second object of the same type is assigned to the first object, then both variables refer to the object at that address.
In order to prevent the original object being updated, you'll have to copy the object before it is manipulated (I'd copy it before passing it, but you could copy it at the target site, too). If your class contains value type properties only, a shallow copy will suffice, i.e. you create a method (or property, but this might be misleading) that returns a new object of your class with all the values copied
class MyClass
{
int Value1 { get; set; }
float Value2 { get; set; }
public MyClass Copy()
{
var copy = new MyClass()
{
Value1 = this.Value1,
Value2 = this.Value2
}
return copy;
}
}
If you object contains reference types itself, you might have to create a deep copy
class MyClass
{
MyClass2 Reference { get; set; }
public MyClass Copy()
{
var copy = new MyClass()
{
Reference = this.Reference.Copy()
}
return copy;
}
}
Of course, those will have to implement a Copy() method, too.
Related
I have a observed a strange behavior today in my application. I have one the methods as follows in my Data Access layer:
public async Task<Order> WriteOrder(Order orderDetails)
{
try
{
Order updatedOrder = GenerateOrderID(orderDetails);
Task insertOrder = orderCollection.InsertOneAsync(orderDetails); // updatedOrder was supposed to be passed here.
// inserts values into Database.
}
}
private Order GenerateOrderID(Order order)
{
try
{
order.Id = Guid.NewGuid().ToString();
order.SubmittedOn = DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss");
return order;
}
catch(Exception ex)
{
throw;
}
}
public class Order
{
[DataMember(Name = "id")]
public string Id { get; set; }
[DataMember(Name = "submittedOn")]
public string SubmittedOn { get; set; }
// some other Data Memebers
}
Explaination :
WriteOrder method is receiving orderDetails as input parameter, then I'm passing this to GeneratedOrderId method , where Id and few other details are updated and the order object is returned. We are receiving the object in updatedOrder object(which we are not using anywhere). Then orderdetails object is passed to InsertOneAsync method and is inserted in database.
All this time I haven't noticed it , but I was supposed to pass updatedOrder as the parameter to InsertOneAsync method, instead of orderDetails.
But somehow it was working, i.e Id and SubmittedOn details as getting updated in the database.When I debugged and saw , orderDetails object is also getting updated (which according to me is not supposed to). Why and how is this happening?
The basic thing to understand here is the when we pass an object of reference type to a method, the reference is passed by value. In your code there is no where a new object of Order has been created.
So what happens is that the state of same object is being updated wherever you are changing the values of properties. There is just single object in memory and you have different copies which point to the same reference.
Which means that in following method call:
private Order GenerateOrderID(Order order)
the order variable is holding the same object which is pointed by orderDetails at caller end. Two copies of reference but pointing to same object in memory.
A small change in your method will completely change the behaviour, if you create new object of Order in the private method. then you will see that the passed object does not gets updated. See:
private Order GenerateOrderID(Order order)
{
try
{
order = new Order(); // note this line
order.Id = Guid.NewGuid().ToString();
order.SubmittedOn = DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss");
return order;
}
catch(Exception ex)
{
throw;
}
}
Now we have a new object out there which we set it's state. Now the updatedOrder and orderDetails both are reference to different objects and changing state of one will not result is changing the other.
Note that this applies to reference types in c# i.e. classes and interfaces objects, not to value types lile int , long, struct etc
From learn.microsoft.com:
A variable of a reference type does not contain its data directly; it
contains a reference to its data. When you pass a reference-type
parameter by value, it is possible to change the data belonging to the
referenced object, such as the value of a class member.
You are passing a reference to orderDetails object to GenerateOrderID method. So the object that you are modifying in this method is still the same object. GenerateOrderID execution changes the state of this object. So when you are passing orderDetails to InsertOneAsync method, the reference you are passing references changed object that you were modifying in GenerateOrderID.
I guess you were thinking that when you pass the orderDetails, you were passing a copy of the object - but it is not true. You are passing the copy of the reference. So if you would assign something to the order reference inside of the method, the orderDetails in WriteOrder method reference would still reference the old object. But here you are changing some fields of the object that you are pointing to.
I recommend reading this.
I have two objects, yet when i update one, the other item with the same GUID gets updated as well even though i am trying to apply a new GUID so that i can separate them out.
var completedSecond = model.CompletedTransfers.First();
var transferedTransfers = model.TransferedTransfers.First();
if (transferedTransfers.Count != 0) {
transferedTransfers.RowId = Guid.NewGuid();
}
When i run this code, the two items have the same GUID, yet when i update the second one to have a new GUID, the first object also gets that new GUID. How is this possible?
You don't have 2 objects but 2 references to the same object in memory. In C# classes are reference types meaning that your completedSecond is referencing an object in memory, that in this case is the same as the object transferedTransfers is referenceing to.
In order to get 2 different object you need to instantiate a new object
By implementing the ICloneable interface
public class MyClass : ICloneable
{
public Guid Id { get; set; }
public object Clone()
{
return new MyClass { Id = Id };
}
}
Another way is to have a copy constructor:
public class MyClass
{
public Guid Id { get; set; }
public MyClass() { }
public MyClass(MyClass other)
{
Id = other.Id;
}
}
Read this for the differences between the two ways: Copy constructor versus Clone()
In addition, when talking about coping of objects you have what is called deep-copy and shallow-copy. More on these you can find here:
Shallow copy or Deep copy?
Object.MemberwiseClone Method
That happens because the model.TransferedTransfers object is passed by reference, not by value. the variable completedSecond and transferedTransfers both points to model.CompletedTransfers. Also be aware of naming smells (transferedTranfers)
This is a very basic question. I am debugging some memory leaks and got totally confused. Suppose I have the following:
public class ObjectData : IDataObject
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ObjectRepository<T> where T : IDataObject
{
private Dictionary<int, T> Objects;
public ObjectRepository()
{
Objects = new Dictionary<int, T>();
// load Data
}
public T GetDataObject(int id);
{
return Objects[id];
}
public Reset()
{
Objects = new Dictionary<int, T>();;
}
}
Now suppose I have the following program flow:
public Main()
{
var DataRepository = new ObjectRepository<ObjectData>();
// Constructor called and Data loaded
var myObject = DataRepository.GetDataObject(1);
DataRepository.Reset();
// Call manually the garbage collector or leave it
// Program flow continue after this
}
The question is, will the garbage collector get rid of the collection initially created by the constructor? Or it will not because one of the elements is still referenced in the program flow (myObject)?
It will be collected (eventually), since there is no more references to it. Getting a reference to something in a dictionary doesn't give you any reference to the dictionary itself! Unless that object somehow references the dictionary internally, that is.
To answer such questions ask yourself: Who is referencing the object in question (in this case the overwritten dictionary)?
DataRepository is not. You overwrote the object reference pointing to the old dictionary.
myObject is not because ObjectData does not have any field of type dictionary. It can't reference a dictionary.
Nobody is left to reference the old dictionary.
After your call to Reset, there is no strong references to your initial dictionary. Thus, it will be elected for garbage collection.
Or it will not because one of the elements is still referenced in the program flow (myObject)?
It doesn't matter which objects the dictionary refers to, what matters is who refers to the dictionary. In this case, no one. It's perfectly possible for the dictionary to be collected while its contents are still alive.
I have a class that uses another class.
The first class have this method:
public void myMethod()
{
//Parameters are an enumeration.
// Really is a exchange variable between this class
//and the other class.
Paramters myParameter = Parameters.Option1;
MyClass2 myOtherClass = new MyClass2(myParameter);
}
The second class:
public enum Parameters { Option1, Option2, Option3 }
MyClass2
{
Parameters _myParameters;
Public MyClass2(Parameters paramParameters)
{
_myParameters = paramParameters;
}
private void clickButton()
{
_myParameters = Parameters.Option2;
this.Dispose();
}
}
What I what it is create a dialog and Parameters are an enumeration that is to serve as exchange between the main window and the dialog to notify about the selection in the dialog.
However, when in the clickButton I change the value of the _myParameters, it is not changed in the object that was passed as parameter in the constructor of MyClass2.
If instead of using an enumeration as exchange variable I create a class that has the enumeration, then I can get the selection. The exchange class would be like this:
class MyExchangeClass
{
Parameters myOption;
}
Then the code would be:
public void myMethod()
{
//Parameters are an enumeration.
// Really is a exchange variable between this class
//and the other class.
MyExchangeClass mySelection= new MyExchangeClass();
MyClass2 myOtherClass = new MyClass2(mySelection);
}
The second class:
public MyExchangeClass
{
Parameters enum MySelection { Option1, Option2, Option3 }
}
class MyClass2
{
MyExchangeClass _mySelection;
Public MyClass2(MyExchangeClassparamParameters)
{
_mySelection= paramParameters;
}
private void clickButton()
{
_mySelection.MySelection = Parameters.Option2;
this.Dispose();
}
}
In this way, the Class1, the main window, gets the updated value in the property of the class MyExchangeClass.
I would like to know why in the first solution the enumeration is not updated, because if it would possible, I would like to avoid the needed to wrap the enumeration in a class.
However, when in the clickButton I change the value of the _myParameters, is not changed in the object that was passed as parameter in the constructor of MyClass2.
No, it wouldn't be. The value was passed in by value - the two variables (myParameter and _myParameters) are independent variables. A change to one variable does not affect the other variable. This is how all types work in C#.
For changes to a parameter within a method to be seen by the caller, you could use a ref parameter, but that's not viable in your case as you're changing an instance variable which was originally populated via a parameter.
You could wrap the value in a mutable class, pass a reference to an instance of that class into MyClass2, and then mutate the object within MyClass2 - that change would be seen within your first class, because that would be changing the data within the object rather than the instance variable of MyClass2. It's hard to know whether or not that's actually a good solution though, as we have so little context - with names like MyClass and myMethod we have no clue as to the bigger picture of what this is trying to achieve.
In your first solution the value of the enumeration inside the class didn't change because enumeration is a value type, and this line:
_myParameters = paramParameters;
made a copy of paramParameters and _myParameters is a completely separate, standalone object.
In your second example, MyExchangeClass is a reference type, so this line:
_mySelection= paramParameters;
made _mySelection point to exactly the same object as paramParameters reference was referring to.
From the documentation:
Variables that are based on value types directly contain values. Assigning one value type variable to another copies the contained value. This differs from the assignment of reference type variables, which copies a reference to the object but not the object itself.
And an enumeration is a value type, ibidem:
The value types consist of two main categories:
Structs
Enumerations
I want to have multiple objects share a reference through a private field, such that any of the objects can assign to the field, and the updated field will be seen by other objects sharing that reference. What I was originally hoping to do was this:
class SomeObject
{
private ref DataObject _data;
public SomeObject(ref DataObject data)
{
_data = ref data; // or something similar
}
public ChangeData(DataObject newData)
{
_data = data;
// at this point, *other* SomeObject instances that were
// created with the same reference should also have _data == newData
}
}
But of course you can't use ref that way: ref is only for method parameters. And a static field won't work, since not all SomeObject instances should refer to the same object---rather, the object in question should be set in the constructor.
Obviously I could solve this by just adding a simple wrapper class. But is there a better way? Is there some kind of SharedReference<T> class that I can use?
Update, since most of the answers misunderstood what I was asking. I know that the _data field contains a reference to the original DataObject. What I want is another level of indirection. I want to be able to change which object I'm pointing to in one instance, and have the new value be picked up by other instances that were created with the same reference. As the updated code sample shows, I want to assign to _data, and effectively change the value of _data in other instances.
I don't know of any class that you can use for this, but I seems quite easy to implement your own SharedReference<T> class.
Something like this:
public sealed class SharedReference<T>
where T : class
{
public T Reference
{
get; set;
}
}
You could simply use an array of shared objects, and reassign the array elements:
class SomeObject
{
// you probably want to make this readonly
private readonly DataObject[] _data;
public SomeObject(DataObject[] data)
{
_data = data;
}
public void ChangeData(DataObject newData)
{
_data[0] = o;
}
// and you could define your own accessor property...
private DataObject Data
{
get { return _data[0]; }
set { _data[0] = value; }
}
}
Apart from that, I think you'll need to define your own 'holder' class & use that
If you simply provide a reference to the object without the ref keyword, you will get the behaviour you want. Using ref is actually passing a reference to a reference (pointer to a pointer), so unless you want to null someone else's reference, it won't be of any use to you.
Update: Sorry I didn't spot that you wanted to re-assign a completely new object into the field and have that reflected throughout. You are best actually creating either a wrapper class to contain the object state and modify that, or a common event that all instances can subscribe to such that when you want to change the object, fire the event with the new object inside it and have each instance update it's own internal reference.
Alternatively, use the Singleton pattern - everyone accesses a publicly available static reference, but unlike the traditional Singleton, you let them change the reference if they want to - so everyone can see the change. This also has the benefit that the objects don't need internal references.
Alternatively again, have the data class expose a method allowing it to consume another data class and copy its state across - like cloning.
In C#,
class SomeObject
{
private DataObject _data;
public SomeObject(DataObject data)
{
_data = data;
}
}
in fact does exactly what you want, if DataObject is a Reference Type, which is true for all classes.
Please disregard this answer since I misunderstood the initial question.Other answers here cover the topic fully.