Altering references within a reference parameter - c#

I am trying to pass an array to a method. The array contains objects which need to be nulled. The method would simply null each object in a loop. I need this to reflect back in the caller.
Sample code (code goodness & minor syntactical issues can be ignored):
public class ABC
{
...
}
private void SomeMethod()
{
var toBeNulledObj1 = new ABC();
var toBeNulledObj2 = new ABC();
var arrayOfNullableObjects = new ABC[]{toBeNulledObj1 ,toBeNulledObj2};
NullingFunction(arrayOfNullableObjects);
}
private void NullingFunction(ABC[] arrayOfNullableObjects)
{
for(int i = 0; i< arrayOfNullableObjects.Length ; i++)
{
arrayOfNullableObjects[i] = null;
}
}
Clearly upon returning, toBeNulledObj1 & toBeNulledObj2 are not null but retain their older values though arrayOfNullableObjects now has two null objects. I realise that ref & out only apply to the collection parameter (here, arrayOfNullableObjects which doesn't even need a ref). I tried passing them in as params instead of a collection but that doesn't help, either (ref & params cannot be combined).
Question: How can I alter each/any object in a collection of objects within a method such that the change is visible to the caller? I am not altering the collection itself. Please note, I am not changing the contents/members of toBeNulledObj1 but the reference itself (to either null or a new object).

Solution #1: Unsafe code
One solution is using a unsafe code. You have to think twice before using it, and I do not know if you will be happy with my answer, but here it is.
static private void SomeMethod()
{
ABC toBeNulledObj1 = new ABC();
ABC toBeNulledObj2 = new ABC();
IntPtr[] arrayOfNullableObjects = new IntPtr[] { MakeReference(ref toBeNulledObj1), MakeReference(ref toBeNulledObj2) };
NullingFunction(arrayOfNullableObjects);
}
static private void NullingFunction(IntPtr[] arrayOfNullableObjects)
{
foreach (IntPtr reference in arrayOfNullableObjects)
ClearReference(reference);
}
/// <summary>
/// Makes the reference to the reference value of a reference type.
/// </summary>
static unsafe private IntPtr MakeReference<T>(ref T value)
where T: class
{
TypedReference reference = __makeref(value);
return *(IntPtr*)&reference;
}
/// <summary>
/// Clears the reference to a reference type, using a reference to that reference value.
/// </summary>
static unsafe private void ClearReference(IntPtr reference)
{
if (sizeof(IntPtr) == 4)
*((int*)reference) = 0;
else
*((long*)reference) = 0;
}
Solution #2: Anonymous class
The second solution could be done by using an anonymous class which holds your data. The fields inside this anonymous class are cleared. A disadvantage is that you have a second class and the reference to this class also should to be cleared. (This can be done by adding ref to o and in the NullingFunction set o to null.) Of course you can also use a predefined class, but his solution is the closest to your code in your OP.
public static void SomeMethod()
{
var container = new
{
toBeNulledObj1 = new ABC(),
toBeNulledObj2 = new ABC(),
};
NullingFunction(container);
}
private static void NullingFunction<T>(T container)
where T : class
{
if (container == null)
return;
foreach(FieldInfo f in container.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic))
if (f.FieldType.IsClass)
f.SetValue(container, null);
}

When you say you want to set them to null, do you mean that you want to destroy the object?
C# has automatic garbage collection, so as soon as an object goes out of scope (that is, when no other objects make reference to it), the garbage collector will destroy it.
In the code above, the label "tobeNulledObj1" still refers to an object, and your array also points to it before you call the NullingFunction.
After you call the NullingFunction, you still have one reference pointing to the object (that is, tobeNulledObj1). If you set tobeNulledObj1 to null, then the Garbage Collector will collect it.
EDIT: I second cheedep's question - what is it exactly that you are trying to do? What do you want your variables to hold at the end?

If function A holds a reference to a variable i.e:
var toBeNulledObj1 = new ABC();
var toBeNulledObj2 = new ABC();
And does not pass this into function B:
private NullingFunction(ABC[] arrayOfNullableObjects)
Then there is nothing that function B can do to change the reference that toBeNulledObj1 / 2 points to.
Since ref is not allowed along with params (as you mentioned):
private void NullingFunction(ref params ABC[] arrayOfNullableObjects)
{
for (int i = 0; i < arrayOfNullableObjects.Length; i++)
{
arrayOfNullableObjects[i] = null;
}
}
The available alternative is to create overloads e.g.:
private void SomeMethod()
{
var toBeNulledObj1 = new ABC();
var toBeNulledObj2 = new ABC();
NullingFunction(ref toBeNulledObj1, ref toBeNulledObj2);
Console.ReadKey();
}
private void NullingFunction(ref ABC one)
{
one = null;
}
private void NullingFunction(ref ABC one, ref ABC two)
{
one = null;
two = null;
}

Is wrapping acceptable?
class Wrapped<T> where T : new() {
private T val = new T();
...
public void Nullify() { val = null; }
}
private void SomeMethod()
{
var toBeNulledObj1 = new Wrapped<ABC>();
var toBeNulledObj2 = new Wrapped<ABC>();
var arrayOfNullableObjects = new Wrapped<ABC>[]{toBeNulledObj1 ,toBeNulledObj2};
NullingFunction(arrayOfNullableObjects);
Debug.Assert(toBeNulledObj1.Get() == null);
// Or...
Debug.Assert(toBeNulledObj1.IsDefined == false);
// Or...
Debug.Assert(toBeNulledObj1.IsNull == true);
}
private void NullingFunction(Wrapped<ABC>[] arrayOfNullableObjects)
{
for(int i = 0; i< arrayOfNullableObjects.Length ; i++)
{
arrayOfNullableObjects[i].Nullify();
}
}
(Disclaimer: hand-compiled code :) may contain errors)
If you need it as a general pattern, you can make NullingFunction parametric (T, U), with a constaint where U: Wrapped<T>
The idea is to make something similar to Nullable for ref types, or something that looks like a smart pointer, if you are familiar with C++.
Thus, wrapper could have T Get() (or an implicit conversion to T) to get out the value, an IsDefined property, and so on.

Related

Are binary tree nodes passed by reference?

static int GetCount(BinTreeNode<int> L)
{
int count = 0;
while (L != null)
{
count++;
L = L.GetRight();
}
return count;
}
I have the method GetCount().
Now, BinTreeNode is a class that has the members: right, left and info.
for some reason, in the main function
static void Main(string[] args)
{
var t2 = new BinTreeNode<int>(null, 1, new BinTreeNode<int>(null, 2, new BinTreeNode<int>(null, 3, null)));
//Console.WriteLine(TreeLessThanTree(t1, t2));
Console.WriteLine(GetCount(t2));
Console.WriteLine(t2.GetInfo()); // this prints 1
Console.ReadKey();
}
This is weird, since in the function GetCount(), L (which is passed by reference) is changed to null.
Why wasn't t2 passed by reference? Why didn't t2 change?
The reference is passed by value. Meaning: the value of t2 is essentially just a pointer that happens to be the address of an object; when it is passed by value, the pointer (i.e. 4 or 8 bytes) is copied and passed in. It doesn't matter what the GetCount does with the local copy of the pointer: the change to the pointer is not observed at the call-site. However, if the method changes the object at the end of the pointer, then those changes will be observable.
The object is passed in the method. Every changes done in the properties of that object will be kept.
However, if you set something else to the local variable in the method, the original object won't be changed. If you want to change the variable, you have to explicitely use the ref keyword.
In example :
public class A
{
public string Foo { get; set; }
}
public class Program
{
// This is a local variable. If you set something else to this one, the original won't be changed
// |
// V
public static void DoSomething(A a)
{
a.Foo = "42"; // <---- This changes the property in the original
a = new A // <---- This sets something to the local variable a
{
Foo = "Hello world"
};
}
// This variable is passed by references. If you set something else to this one, the original will be changed
// | |
// V V
public static void DoSomethingElse(ref A a)
{
a.Foo = "42"; // <---- This changes the property in the original
a = new A // <---- This sets something to the original
{
Foo = "Hello world"
};
}
public static void Main()
{
var a = new A
{
Foo = "Bar"
};
DoSomething(a);
Console.WriteLine(a.Foo); // This outputs 42
// notice this --v
DoSomethingElse(ref a);
Console.WriteLine(a.Foo); // This outputs Hello world
}
}

"Reflection": Method with "params" does not initialize my custom Objects

I have a custom class from which I create over 200 objects for a Gui.
In my main class with my business logic I want to apply the instantiation of all my objects, attach them to an event handler and set their Name. Instead of doing all this by hand for every object I thought to create a method that takes as parameters a "params" list of my objects. The problem is that this method seems to work out a "copy" of my objects instead the reference of those. What I have so far is:
My object base class:
public class MyObject
{
... // this works
}
My business class now (what works):
public class Logic
{
public MyObject Object001, Object002,... Object200; // note that they are not instantiated yet
public Logic()
{
Object001 = new MyObject();
Object001.Name = nameof(Object001);
Object001.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
Object002 = new MyObject();
Object002.Name = nameof(Object002);
Object002.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
...
Object200 = new MyObject();
Object200.Name = nameof(Object200 );
Object200.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
}
}
My desired business class (what has not worked):
public class Logic
{
public MyObject Object001, Object002,... Object200; // note that they are not instantiated yet
public Logic()
{
InstantiateAllObjects(Object001, Object002, ..., Object200); // here my 200 objects!
}
private void InstantiateAllObjects(params MyObject[] list)
{
for (int i=0; i<list.Length; i++)
{
// if(list[i]==Object001) Console.WriteLine("Object001== null?: " + (Object001 == null)); --> this executes for EVERY object, instead for only Object001!!
MyObject obj = list[i];
obj = new MyObject();
obj.Name = nameof(obj); // why "nameof(list[i])" didn't work?
obj.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
// if (list[i] == Object001) Console.WriteLine("Object001== null?: " + (Object001== null)); --> this executes again for EVERY object AND (Object001== null) is ALWAYS true!!
}
}
}
Can anybody explain me why my method seems not to create my objects?
Thanks in advance for your time and help!
EDIT
In my opinion, the problem seems to be i have to pass a reference to these declared objects in the 'params' list of my method... is it possible? how? I tried to use the modifiers "out" and "ref" near the "params" in the method, but with "params" seems not to be possible...
EDIT2
Following some suggestions I created in my class: logic an object list:
MyObject[] list = new MyObject[] { Object001, Object002, ..., Object200 };
InstantiateAllObjects(ref list);
and modified my method as per private void InstantiateAllObjects(ref MyObject[] list) iterating inside over list[i], but unfortunately with the same wrong result...
and also tried
List<MyObject> list = new List<MyObject>() { Object001, Object002, ..., Object200 };
InstantiateAllObjects(ref list);
and modified my method as per private void InstantiateAllObjects(ref List<MyObject> list) iterating inside over list[i], but unfortunately also with the same wrong result...
Forget about all those Object001, Object002 etc fields. Just use a new List<MyObject>() and add your objects into it inside the for-loop in your InstantiateAllObjects.
public class Logic
{
private readonly List<MyObject> allMyObjects; // note that they still are not instantiated yet
public Logic()
{
cont int amount = 200;
allMyObjects = new List<MyObject>(amount); // reserve space, but all are still null
InstantiateAllObjects(allMyObjects, amount);
}
private void InstantiateAllObjects(List<MyObject> list, int amount)
{
for (int i=0; i<amount; i++)
{
MyObject obj = new MyObject();
obj.Name = "Object" + (i+1).ToString("000");
obj.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
list.Add(obj); // place the newly created object in the list
}
}
}
Your original test if(list[i]==Object001) fires every time, because both list[i] (for every i) and Object001 are null.
Also note that you can have multiple references to one instance of your MyObject (this already happens when you pass the reference as parameter to some method). The fact that one of those is called "Object001" is not important and in fact unknown to the instance. That is why nameof(list[i]) cannot return "Object001".
Another try, based on Hans Kesting'a answer and anna's statement
2) the name of every object will be (very) different one to another,
it is here just that i used the indexes 001...200 to concept the idea,
To avoid any trouble with accessing the fields and to regard what you wrote in the comments, no getter/setter is used and everything is public. Not the best approach but well, compared to handling 200 separate objects...
public class Logic
{
public List<MyObject> MyObjectList;
public List<string> MyObjectNames;
public Logic()
{
var anotherClass = new AnotherClass();
MyObjectNames = new List<string>() {"Object01", "Object02", "Object03"}; // either add your names here...
MyObjectNames.Add("Object04"); // or add additional names this way
//MyObjectNames.AddRange(anotherNameList); // or add another list or use Linq or whatever
MyObjectList = anotherClass.InstantiateAllObjects(MyObjectNames);
}
}
public class AnotherClass
{
public List<MyObject> InstantiateAllObjects(List<string> nameList)
{
var objectList = new List<MyObject>(nameList.Count);
foreach (var name in nameList)
{
objectList.Add(new MyObject(){Name = name});
}
return objectList;
}
}
Does this meet your requirements?
If you prefer a Dictionary, it's similar:
public class Logic
{
public Dictionary<string, MyObject> MyObjectDict;
public List<string> MyObjectNames;
public Logic()
{
var anotherClass = new AnotherClass();
MyObjectNames = new List<string>() { "Object01", "Object02", "Object03" }; // either add your names here...
MyObjectNames.Add("Object04"); // or add additional names this way
//MyObjectNames.AddRange(anotherNameList); // or add another list or use Linq or whatever
MyObjectDict = anotherClass.InstantiateAllObjects(MyObjectNames);
// objects in dict can be accessed directly by their names:
var object01 = MyObjectDict["Object01"];
}
}
// You can access in derived classes or any other classes
public class DerivedLogic : Logic
{
public void SomeFunc()
{
var object01 = MyObjectDict["Object01"];
}
public void SomeOtherFunc(string objectName)
{
var object01 = MyObjectDict[objectName];
}
}
public class AnotherClass
{
public Dictionary<string, MyObject> InstantiateAllObjects(List<string> nameList)
{
var objectList = new Dictionary<string, MyObject>(nameList.Count);
foreach (var name in nameList)
{
// check if object with name does not already exist.
if(!objectList.ContainsKey(name)
{
// For your property changed assignment, you can separate the object creation and DIctionary/List assignment
var obj = new MyObject() { Name = name };
obj.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
objectList.Add(name, obj);
}
// else .... doe something
}
return objectList;
}
}
All of those arguments are being passed by value. You're passing the references, but the InstantiateAllObjects method can't change the values of any of the fields.
Simplifying this a bit, it's a little like having code like this:
string x = "Original value";
// This copies the value of x into the array
string[] array = new string[] { x };
// This changes the array element, but doesn't affect the x variable at all
array[0] = "Different value";
// Still prints "Original value", because x hasn't changed
Console.WriteLine(x);
As noted in comments, using a list is likely to be a much better approach than lots of different fields here.
Use a class (can be the same as the one with the logic) to hold your MyObject Fields, pass it into a function that uses reflection to populate the fields.
// Class with Fields
public class MyObjectCollection
{
public MyObject Object001;
public MyObject ObjTwo;
}
public class Logic
{
// Init logic
public void Initialise(object controls)
{
Type targType = typeof(MyObject);
var t = controls.GetType();
// iterate all fields of type MyObject
foreach(var fi in t.GetFields().Where(f=>f.FieldType == targType))
{
// initialise as required.
var o = new MyObject();
o.Name = fi.Name;
fi.SetValue(controls, o);
}
}
}
If the field name is not enough for the object name you could use Attributes on the fields to direct initialisation.
This should do the job. It is a reflection-based approach and what it does is:
find all public instance fields
use only those whose name contains Object
create a new instance of MyObject and store that in the field
public void InstantiateAllObjects()
{
foreach (FieldInfo field in this.GetType()
.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.Name.Contains("Object")))
{
MyObject obj= new MyObject();
obj.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
field.SetValue(this, obj);
}
}
If you add above method to your Logic class, you can then call it without having to explicitly pass Object001, Object002 etc. to it.
public class Logic
{
public MyObject Object001, Object002, ... Object200;
public Logic()
{
this.InstantiateAllObjects();
}
}
EDIT: Differently named fields
If the fields do not share a common prefix (that is, if they are not all named Object + ...), there are other ways to get the fields:
.Where(x => x.FieldType == typeof(MyObject))
yields only the fields whose type is MyObject.
You could also create an attribute
[AttributeUsage(AttributeTargets.Field)]
public sealed class FieldIWantToSetAttribute : Attribute { }
and apply that to all fields you want to set, e.g.
public class Logic
{
[FieldIWantToSet] public MyObject Object001; // will be set
[FieldIWantToSet] public MyObject Foo; // will be set
public MyObject Bar; // will not be set
}
Then, change the Where to
.Where(x => x.GetCustomAttribute<FieldIWantToSet>() != null)
Please however note that 1. you should definitely use caching. Reflection without caching is expensive, and 2. please overthink your design - why exactly do you feel the need to expose 200 fields for every other class to see?

Do anonymous methods passed to actions obtain data by value or reference?

I am creating an anonymous method and passing it into an action to be invoked later on. I would like to pass some numeric data (int) into my anonymous method. In order to pass the data by value, am I required to create copies? Or, will the data be passed by value?
Here is what I think the implementation would look like, if I had to create copies:
private void CreateAction()
{
int bus = 4;
CustomObject[] data = new object[16];
int length = 1500;
this.doWorkLater = new Action(() =>
{
var busCopy = bus;
var dataCopy = data;
var lengthCopy = length;
this.WorkMethod(busCopy, dataCopy, lengthCopy);
});
}
Is this (the above code) necessary in order to get length and bus by value?
In this case, will CustomObject[] data (some class I have created) be passed by reference or by value?
What you pass is not a By-Value Copy.
If you're not going to modify the values before executing the action, then you don't need to worry about how you pass the values. But no they are no passed by value. Even if you return the action and Invoke it from another method. the values are persisted in a class generated by the compiler. No need to worry about that
If you expect the data to change before executing the action then you're doing it wrong. The approach you're using (Copy a Value-Type to a local variable) should be done outside the action and not inside of it. As for the reference type (Array) even if you copy it to a local variable, the reference it copied so any change in the copy local variable is reflected.
private void CreateAction()
{
int bus = 4;
CustomObject[] data = new object[16];
int length = 1500;
var busCopy = bus; // a copy of bus
var dataCopy = data; // reference copy
var lengthCopy = length; // a copy of length
this.doWorkLater = new Action(() =>
{
this.WorkMethod(busCopy, dataCopy, lengthCopy);
});
bus = 10; // No effect on the action
length = 1700; // No effect on the action
this.doWorkLater();
}
This looks pointless, but you may sometimes need to copy a local variable to another local variable before passing it to an anonymous method. Check this Valid Example that fixes a reported unexpected behavior !
Closures capture values observably by reference* - note that code you have does not solve issue for general case, also if whole point of CreateAction is create one single action it will work.
private void CreateAction()
{
int bus = 4;
this.doWorkLater = new Action(() =>
{
var busCopy = bus;
this.WorkMethod(busCopy);
});
// if you change local `bus` before call to `doWorkLater` it will not work:
bus = 42;
doWorkLater(); // busCopy is 42.
}
* It actually collects all variables in a compiler created class and uses reference to it to access variables in a method and closure. Thus even value types look behave as if passed by reference.
This might help you to figure out what's going on.
If you start with this slightly simplified class:
public class Example
{
private void CreateAction()
{
int bus = 4;
object[] data = new object[16];
int length = 1500;
Action doWorkLater = () =>
{
var busCopy = bus;
var dataCopy = data;
var lengthCopy = length;
this.WorkMethod(busCopy, dataCopy, lengthCopy);
};
doWorkLater.Invoke();
}
public void WorkMethod(int bus, object[] data, int length)
{
}
}
...then the compiler produces basically this:
public class Example
{
private void CreateAction()
{
Example.GeneratedClass closure = new Example.GeneratedClass();
closure.parent = this;
closure.bus = 4;
closure.data = new object[16];
closure.length = 1500;
// ISSUE: method pointer
IntPtr method = __methodptr(closure.CreateAction);
new Action((object)closure, method)();
}
public void WorkMethod(int bus, object[] data, int length)
{
}
[CompilerGenerated]
private sealed class GeneratedClass
{
public int bus;
public object[] data;
public int length;
public Example parent;
internal void CreateAction()
{
this.parent.WorkMethod(this.bus, this.data, this.length);
}
}
}
The local variables trapped in the closure cease being local variables to the method and become public fields in the generate class.
Now everything that you know about C# and classes applies.

Self-contained generic memento

Dearest fellow programmers,
I seem to lack some understanding as of how the referencing works in C#.
The case:
I tried to implement some sort of Memento proxy which would wrap an interface and store every parameter that we're provided to the method calls and store these into a list.
Whenever necessary we could call the RestoreState and the objects would "reset" to the original state.
The code:
Consumer and model object
class Program
{
static void Main(string[] args)
{
IMemento memento = new Memento();
PrestationInfo prestationInfo2 = new PrestationInfo { Advance = 2 };
memento.Add(prestationInfo2);
Console.WriteLine(prestationInfo2.Advance); //Expect 2
prestationInfo2.Advance = 1;
Console.WriteLine(prestationInfo2.Advance); //Expect 1
memento.RestoreState();
Console.WriteLine(prestationInfo2.Advance); //Expect 2, but still 1
Console.ReadKey();
}
}
[Serializable]
public class PrestationInfo
{
public int Advance { get; set; }
}
Memento
public interface IMemento
{
void Add(object pItem);
void RestoreState();
}
public class Memento : IMemento
{
public Memento()
{
MementoList = new Dictionary<long, object>();
ReferenceList = new List<object>();
ObjectIDGenerator = new ObjectIDGenerator();
}
private ObjectIDGenerator ObjectIDGenerator { get; set; }
private Dictionary<long, object> MementoList { get; set; }
private List<object> ReferenceList { get; set; }
public void Add(object pItem)
{
bool firstTime;
long id = ObjectIDGenerator.GetId(pItem, out firstTime);
if (firstTime)
{
var mementoObject = DeepCopy(pItem);
MementoList.Add(id, mementoObject);
ReferenceList.Add(pItem);
}
}
public void RestoreState()
{
for (int i = 0; i < ReferenceList.Count; i++)
{
object reference = ReferenceList[i];
bool firstTime;
long id = ObjectIDGenerator.GetId(reference, out firstTime);
if (MementoList.ContainsKey(id))
{
object mementoObject = MementoList[id];
reference = mementoObject;
//reference = PropertyCopy<PrestationInfo>.CopyFrom(mementoObject as PrestationInfo); //Property copy
//Interlocked.Exchange(ref reference, mementoObject); //Also tried this
}
}
}
private static TCopy DeepCopy<TCopy>(TCopy pObjectToCopy)
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, pObjectToCopy);
memoryStream.Position = 0;
return (TCopy)binaryFormatter.Deserialize(memoryStream);
}
}
}
Extra info
My guess is, I'm doing/understand something wrong regarding the List.
I also tried the Interlocked.Exchange, playing around with "ref"'s, using WeakReference's and storing the object into a CareTaker object (and storing that CareTaker into the List), implement some copy Property thing...
And ... I just can't see it.
My expected result would be the PrestationInfo.Advance property containing the value 2. But it keeps
Try this:
Change the Add method:
public long Add(object pItem)
{
bool firstTime;
long id = ObjectIDGenerator.GetId(pItem, out firstTime);
if (firstTime)
{
var mementoObject = DeepCopy(pItem);
MementoList.Add(id, mementoObject);
ReferenceList.Add(pItem);
}
return id; // i need my memento! LOL
}
You should also add this accessor method:
public object GetRestoredState(long id)
{
return MementoList[id]; // you should put some range check here
}
Now that you have your id, you can fetch the restored state this way:
memento.RestoreState();
prestationInfo2 = memento.GetRestoredState(savedId); // <-- you got this when you called the Add()...
Console.WriteLine(prestationInfo2.Advance); //Expect 2, but still 1
Follow ups: you can also make the IMemento into a IMemento<T>, and adjust your code accordingly
Memento.Add needs a ref parameter modifier to access the original reference type pointer.
https://msdn.microsoft.com/en-us/library/14akc2c7.aspx?f=255&MSPPError=-2147217396
It looks like the problem is in your understanding of references in .NET
public void RestoreState()
{
for (int i = 0; i < ReferenceList.Count; i++)
{
object reference = ReferenceList[i];
bool firstTime;
long id = ObjectIDGenerator.GetId(reference, out firstTime);
if (MementoList.ContainsKey(id))
{
object mementoObject = MementoList[id];
reference = mementoObject;
//reference = PropertyCopy<PrestationInfo>.CopyFrom(mementoObject as PrestationInfo); //Property copy
//Interlocked.Exchange(ref reference, mementoObject); //Also tried this
}
}
}
The RestoreState method above does not return anything, and you're strictly operating on references, not their internal state. Inside your method object reference is a local reference. It is not the same as the external prestationInfo2 and your method simply makes reference point (refer) to the previously saved copy of the state of presentationInfo2.
You could modify it something like this:
public object RestoreState()
{
for (int i = 0; i < ReferenceList.Count; i++)
{
object reference = ReferenceList[i];
bool firstTime;
long id = ObjectIDGenerator.GetId(reference, out firstTime);
if (MementoList.ContainsKey(id))
{
object mementoObject = MementoList[id];
reference = mementoObject;
return reference;
}
}
return null;
}
And then call it like this:
presentationInfo2 = memento.RestoreState();
If you want the memento to track objects and magically restore their state you will have to make the objects themselves aware of the memento which introduces coupling or use reflection to modify the tracked references internal state. Basically you don't deserialize the persisted state into a new object but use reflection to overwrite the previously stored internal state into the tracked object reference.
Be careful though to store references using WeakReference otherwise you will find yourself with a nice case of memory leak.

c# class variable still null after calling function

After calling func1 variable mydata stays null. In debug mode I see that in func3 it sets data to string. why it doesn't pass value after exiting function?
Class example
class myclass
{
public string mydata;
public int func1()
{
//....
func2(/**/, mydata);
//....
return 1;
}
private int func2(/**/,data)
{
byte[] arr = new byte[1000];
//...
func3(arr,data);
//...
return 1;
}
private void func3(byte[] arr, string data)
{
char[] a = new char[100];
//...
data = new string(a);
}
}
By default, parameters are passed by value; it means that what is passed is actually a copy of the variable (which is a copy of the reference in the case of a reference type like string). When func3 assigns data, it only modifies a local copy of the variable.
Now, if you change func2 and func3 signatures so that data is passed by reference, you will get the expected result:
public int func1()
{
//....
func2(/**/, ref mydata);
//....
return 1;
}
private int func2(/**/,ref string data)
{
byte[] arr = new byte[1000];
//...
func3(arr, ref data);
//...
return 1;
}
private void func3(byte[] arr, ref string data)
{
char[] a = new char[100];
//...
data = new string(a);
}
I suggest you read Jon Skeet's article about parameter passing for more details.
First, it's an instance variable, not a class variable. To be a class variable it would have to be declared static.
Second, why do you hand it down as an argument to the respective functions? The way you're doing it, it creates a separate string in every method and doesn't refer to the original one. Just go ahead and call it directly:
private void func3(byte[] arr)
{
//...
mydata = new string(a);
}
It because all arguments are passed by reference into the method. data = new string(a);
crates new instance of a string with new reference.
var o = new object(); // reference 1
function void method(object something)
{
// here we have reference to something in stack, so if we will assign new value to it, we will work with stack copy of a reference.
something = null; // we removed reference to something in method not initial o instance
}
You're passing the reference to the string mydata by value. This means mydata will still refer to the same object after the function returns, regardless of what you do inside the function. If you wanted to change the string mydata, you could pass the reference by reference:
public int func1()
{
//....
func2(/**/, ref mydata);
//....
return 1;
}
private int func2(/**/, ref string data)
{
byte[] arr = new byte[1000];
//...
func3(arr, ref data);
//...
return 1;
}
private void func3(byte[] arr, ref string data)
{
char[] a = new char[100];
//...
data = new string(a);
}

Categories