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.
Related
I'll try to resume everything as much as possible:
I have a class that creates a dictionary (let's call it secondary dictionary) with the initial values of some of the elements of another dictionary, (for simplicity, let's call it the main dictionary and this class does this only once) and then a thread that checks every x miliseconds against this main dictionary, that's being regularly updated every y miliseconds, for any changes on the elements that where stored in the initialization phase.
My problem is, that when I want to compare the values of element1 in the main dictionary against the value of element1 in the secondary dictionary, they're always the same, (I undersatnd that, until the main dictionary is nnot updated, both values will be the same, but when the main dictionary gets updated, so does the second, immediately, without me doing anything)
I've tried ConcurrentDictionaries, using locks, a combination of boths, creating a new element in the initialize functions instead of passing it directly, but nothing seems to work
class Checker
{
private static bool secondaryDictionaryHasChanged = false;
private static ConcurrentDictionary<int, ValueDTO> secondaryDictionary = new ConcurrentDictionary<int, ValueDTO>();
public Checker()
{
initSecondaryDictionary();
Thread checkChangedValsThread = new Thread(() => checkChangedValsThreadFunction(1000));
checkChangedValsThread.Start();
}
public void initSecondaryDictionary()
{
Object MainLock = new Object();
lock (MainLock)
{
Object secondaryLock = new Object();
lock (secondaryLock)
{
foreach (var variable in Maindictionary)
{
if (variable.isElegibleValue)
{
ValueDTO ValueDTOToAdd = new ValueDTO();
ValueDTOToAdd.EligibleVar = variable;
if (variable.contextVariables.Count > 0)
{
List<Var> contextVariablesToAdd = new List<Var>();
foreach (var item in variable.contextVariables)
{
contextVariablesToAdd.Add(getVarFromMainDictionary(item));
}
ValueDTOToAdd.ContextVars = contextVariablesToAdd;
}
secondaryDictionary.TryAdd(ValueDTOToAdd.EligibleVar.varCode, ValueDTOToAdd);
}
}
}
}
secondaryDictionaryHasChanged = false;
}
public void checkChangedValsThreadFunction(int checkTime)
{
while (true)
{
try
{
if (!secondaryDictionaryHasChanged)
{
Thread.Sleep(checkTime);
Object mainLock = new Object();
lock (mainLock)
{
Object secondaryLock = new Object();
lock (secondaryLock)
{
foreach (var item in secondaryDictionary)
{
ValueDTO secondaryDictionaryDTO = item.Value;
Var variableInSecondary = secondaryDictionaryDTO.EligibleVar;
Var variableInMain = getVarFromMainDictionary(item.Value.EligibleVar.varID);
int valueInMain = variableInMain.getIntValue();
int valueInSecondary = variableInSecondary.getIntValue();
if (valueInMain != valueInSecondary)
{
//IT NEVER ENTERS THIS HERE
}
}
}
}
}
}
catch (Exception e)
{
throw new Exception("Some exception: " + e.Message);
}
}
}
}
internal class ValueDTO
{
public Var EligibleVar { get; set; }
public List<Var> ContextVars { get; set; }
}
internal class Var
{
public int varCode { get; set; }
public string varID { get; set; }
}
I add a reduced version of the code I have the thing is that it will never go inside that if(mainValue != secondaryValue)
any help or info about where I'm going wrong will be deeply appreciated
As fildor already said, you have one object and two references to it.
To simplify your problem I created a short sample:
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
public class Example
{
class Person
{
public string Name
{
get;
set;
}
}
public static void Main()
{
var myPerson = new Person();
myPerson.Name = "User1";
var list1 = new List<Person>();
var list2 = new List<Person>();
// put a reference to myPerson into each of the lists
list1.Add(myPerson);
list2.Add(myPerson);
// get the reference to myPerson from list1
var myPersonInList1 = list1[0]
myPersonInList1.Name = "User2";
// this will print User2 because there is only one person object
Console.WriteLine(list2[0].Name);
}
}
Basically no matter where you pass the Person, it will always be a reference to the Person instance you created. All changes to it will apply to the object behind the reference. All references point to the same object.
A bit more in depth knowledge very simplified, but I hope you'll get the point:
There is a stack and a heap storage. The stack contains value types and references and the heap contains objects.
Stack:
Variable | Value
--------------------
myPerson | #abc123
list1[0] | #abc123
list2[0] | #abc123
myInt | 42
Heap:
Address | Value
-------------------
abc123 | { Name: "User2" }
Now every time you create a variable pointing to myPerson, you only create a new entry in the stack, pointing to the same object in the heap. Now, of course when you update the object behind the reference and set its name to "User1", all references will display the change.
everyone!
Tried to make saver of my List with help of combination JSON and PlayerPrefs but stucked with the problem. It don't save my list and don't load. It only giving me my list with zero elements.
Code:
private List<Toggle> _listActiveToggles = new List<Toggle>();
public void Awake()
{
Load();
foreach (Toggle toggle in _listActiveToggles)
{
toggle.isOn = true;
}
}
public void ChooseUsableBuffs(Toggle toggle)
{
int level = PlayerPrefs.GetInt("HDD") / 10;
if (_listActiveToggles.Any(x => toggle))
{
_listActiveToggles.Remove(toggle);
Debug.Log(_listActiveToggles.Count);
return;
}
if (_listActiveToggles.Count >= level)
{
_listActiveToggles[0].isOn = false;
_listActiveToggles.RemoveAt(0);
}
_listActiveToggles.Add(toggle);
Save();
Debug.Log(_listActiveToggles.Count);
}
public void Save()
{
PlayerPrefs.SetString("Key" ,JsonUtility.ToJson(_listActiveToggles));
}
public void Load()
{
_listActiveToggles = JsonUtility.FromJson<List<Toggle>>(PlayerPrefs.GetString("Key"));
}
Tried to check what JSON contains and it showed only: {}
The JsonUtility can not directly serialized array and list types.
You rather need a wrapper class like
[Serializable]
public class ToggleData
{
public List<Toggle> _listActiveToggles = new List<Toggle>();
}
public ToggleData data;
Which now you can serialize
var json = JsonUtility.ToJson(Data);
and deserilialize
JsonUtility.FromJsonOverwrite(json, data);
However, in your specific case I guess it will not work anyway. UnityEngine.UI.Toggle is of type MonoBehaviour and you can not simply deserilialize these from JSON!
You can only store their settings and loading settings into existing toggles.
I guess actually it should be enough to store a list of bool to JSON and iterate over your toggles and the list and do e.g.
using System.Linq;
...
[Serializable]
public class ToggleData
{
public List<bool> ToggleStates = new List<book>();
}
public ToggleData data;
[SerializeField] private List<Toggle> _listActiveToggles = new List<Toggle>();
And then save via
data.ToggleStates = _listActiveToggles.Select(t => t.isOn).ToList();
var json = JsonUtility.ToJson(Data);
and deserilialize via
JsonUtility.FromJsonOverwrite(json, data);
for(var i = 0; i < Mathf.Min(data.ToggleStates.Count, _listActiveToggles.Count); i++)
{
_listActiveToggles[i].isOn = data.ToggleStates[i];
}
Note however that this means you should reference all Toggles you want to save the state for in your list via the Inspector and can not simply remove or add entries on runtime!
Say I have the following code
public class FooClass
{
public int A { get; set; } = 0;
public int B { get; set; } = 0;
}
public class BarClass
{
public int X { get; set; } = 0;
public int Y { get; set; } = 0;
}
public class MyClass
{
int z = 0;
public FooClass Foo { get; set; } = new FooClass();
public BarClass Bar { get; set; } = new BarClass();
public static void MyMethod()
{
// List of MyClass objects
var myList = Enumerable.Range(1, 10).Select(_ => new MyClass()).ToList();
// Some flags set elsewhere
bool getFooAValues = true;
bool getBarYValues = true;
bool getClassZValues = true;
// Some statements that collects "field referecnes" of MyClass
var classFieldReferenceList = new List<...>();
if (getFooAValues)
classFieldReferenceList.Add(...);
if (getBarYValues)
classFieldReferenceList.Add(...);
if (getClassZValues)
classFieldReferenceList.Add(...);
// For each field reference
classFieldReferenceList.ForEach(classFieldRef =>
{
// For each class
myList.ForEach(myClassInst =>
{
// "Select"/"Apply" the reference to get the field value
var fieldValue = myClassInt.getTheFieldReferenceValue(classFieldRef);
// Do something with field value...
return fieldValue;
});
// Do something with the list of field values...
});
}
}
In this code, specifically in MyMethod, I create a list of MyClass objects. This class has a few fields, some are simply primitive types, some are instances of other classes. How can I refer to or address these fields in the form of some object I can pass around?
For example, I began writing code, akin to the following
public static void MyMethod()
{
// List of MyClass objects
var myList = Enumerable.Range(1, 10).Select(_ => new MyClass()).ToList();
// Some flags set elsewhere
bool getFooAValues = true;
bool getBarYValues = true;
bool getClassZValues = true;
if (getFooAValues)
{
var Avalues = myList.Select(myClassInst => myClassInst.Foo.A);
// Do Action X to list of values
}
if (getBarYValues)
{
var Yvalues = myList.Select(myClassInst => myClassInst.Bar.Y);
// Do Action X to list of values
}
if (getClassZValues)
{
var Zvalues = myList.Select(myClassInst => myClassInst.z);
// Do Action X to list of values
}
}
Where //Do Action X was quite a few lines of code that I would perform to each set of values (Plotting values on a plot, flags represent showing plot line or not). Though, I don't really want duplicate that code for each possible field I could refer/address within MyClass. Thus, I want to refer to a field by some "object" then "apply" that object to an instance of MyClass later to get the value of the field, if that makes sense.
I feel like this might be akin to defining a delegate? Though the delegate would be specific to some class structure?.. Or maybe there is some simple solution I have confused myself out of finding.
You can use Func<MyClass,object> delegate:
var classFieldReferenceList = new List<Func<MyClass,object>>();
if (...)
classFieldReferenceList.Add(m => m.Foo.A);
if (...)
classFieldReferenceList.Add(m => m.Foo.B);
if (...)
classFieldReferenceList.Add(m => m.Bar.X);
if (...)
classFieldReferenceList.Add(m => m.Bar.Y);
This is not ideal because object is used as the most common denominator, but that would be required for a "mixed bag" of types.
In your second example you could get away with a generic method:
private void DoActionsOnSelectedFields<T>(IEnumerable<MyClass> data, Func<MyClass,T> selector) {
foreach (T val in data.Select(selector)) {
... // Perform some common action
}
}
First let me show a simple test case of the problem and how to trigger it. Here is the class:
class ProtoRecurseTest
{
private int nextPayload = 1;
public int Payload { get; private set; } = 0;
public ProtoRecurseTest Back { get; private set; } = null;
public List<ProtoRecurseTest> Children { get; set; } = new List<ProtoRecurseTest>();
public ProtoRecurseTest Add()
{
ProtoRecurseTest result = new ProtoRecurseTest(this, nextPayload++);
Children.Add(result);
return result;
}
public ProtoRecurseTest()
{
}
private ProtoRecurseTest(ProtoRecurseTest parent, int payload)
{
Back = parent;
this.Payload = payload;
nextPayload = payload + 1;
}
private static void ToStringHelper(ProtoRecurseTest proto, StringBuilder sb)
{
sb.Append(proto.Payload + " -> ");
// another little hassle of protobuf due to empty list -> null deserialization
if (proto.Children != null)
{
foreach (var child in proto.Children)
ToStringHelper(child, sb);
}
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
ToStringHelper(this, sb);
return sb.ToString();
}
}
There are no protobuf annotations as that is being taken care of programmatically. I have manually ensured that the class along with Back and Children are all added to the schema with .AsReferenceDefault = true.
The recursion triggering occurs when an instance is populated to a depth of at least 8 bizarrely enough. 7 is fine. Population code is straight forward:
ProtoRecurseTest recurseTest = new ProtoRecurseTest();
ProtoRecurseTest recurseItem = recurseTest;
for (int i = 0; i < 8; i++)
recurseItem = recurseItem.Add();
And then serialize recurseTest. This behavior only occurs when the children are in a list but in a list it occurs even with only 1 child per list as you end up with from the sample populating code. When I replaced the children with a single reference everything serialized fine.
This is using ProtoBuf.NET 2.1.0.0.
Here is a solution, but one I'm not particularly fond of. It was based on #marc-gravell 's answer to this question.
class ProtoRecurseTest : ISerializationManagementCallbacks
{
private int nextPayload = 1;
public int Payload { get; private set; } = 0;
public ProtoRecurseTest Back { get; private set; } = null;
public List<ProtoRecurseTest> Children { get; set; } = new List<ProtoRecurseTest>();
public ProtoRecurseTest Add()
{
ProtoRecurseTest result = new ProtoRecurseTest(this, nextPayload++);
Children.Add(result);
return result;
}
public ProtoRecurseTest()
{
}
private ProtoRecurseTest(ProtoRecurseTest parent, int payload)
{
Back = parent;
this.Payload = payload;
nextPayload = payload + 1;
}
private static void ToStringHelper(ProtoRecurseTest proto, StringBuilder sb)
{
sb.Append(proto.Payload + " -> ");
// another little hassle of protobuf due to empty list -> null deserialization
if (proto.Children != null)
{
foreach (var child in proto.Children)
ToStringHelper(child, sb);
}
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
ToStringHelper(this, sb);
return sb.ToString();
}
static void PreSerializationHelper(ProtoRecurseTest instance)
{
instance.Back = null;
foreach (var child in instance.Children)
PreSerializationHelper(child);
}
public void BeforeSerialization()
{
PreSerializationHelper(this);
}
static void PostDeserializationHelper(ProtoRecurseTest instance, ProtoRecurseTest parent)
{
if (instance.Children == null)
instance.Children = new List<ProtoRecurseTest>();
instance.Back = parent;
foreach (var child in instance.Children)
PostDeserializationHelper(child, instance);
}
public void AfterDeserialization()
{
PostDeserializationHelper(this, null);
}
}
The calls to serialize/deserialize now simply check if the type can be casted to the ISerializationManagementCallbacks (which was just provide a BeforeSerialization and AfterDeserialization method) before doing their business and then calling the appropriate methods if so. And it works fine.
However, I'm not really fond of mixing ever more serialization issues into my codebase - that's actually why the schema is generated programmatically. Ideally I'd like to separate the serialization entirely but the empty list -> null issue (which not to say it's an "issue" per se but just an undesirale part of the protobuf standard) already makes that impossible, but this would be another even more esoteric issue to work around and I do think this one might indeed be an issue.
How to pass by ref different types of objects that have the same properties inside and populate them without interface.
In my app there are totally different types which have some properties in common.
Let's say this properties are double arrays.
double[] samples;
Now I have to populate these samples for 20 objects.
I don't have any access to the class definition of this object, so I can't make interface, or make them inherit from a base class.
How can use one method which I call and this method to populate all my properties.
I want to have one method like this:
private static void FillSamples(ref WhoKnowsWhat dataType, MyObject theSamples)
{
for (int i = 0; i < sampleCount; i++)
{
dataType.SampleLength[i] = MyObject.X[i];
dataType.SampleValue[i] = MyObject.y[i];
}
}
And call this with totally different types.
FillSamples(ref BigStruct.OneTypeMine, theSamples);
FillSamples(ref BigStruct.AnotherTypeMine, theSamples);
FillSamples(ref BigStruct.HisType12345, theSamples);
Then the big struct should have these samples filled in the end.
Is there a way in C#?
Thanks!
You can use the dynamic keyword:
private static void FillSamples(dynamic dataType, MyObject theSamples)
{
for (int i = 0; i < sampleCount; i++)
{
dataType.SampleLength[i] = MyObject.X[i];
dataType.SampleValue[i] = MyObject.y[i];
}
}
Edit:
Using reflection (if you don't use .Net 4.0 or higher):
private static void FillSamples(object dataType, MyObject theSamples)
{
Type t = dataType.GetType();
var px = t.GetProperty("SampleLength");
var py = t.GetProperty("SampleValue");
for (int i = 0; i < sampleCount; i++)
{
px.SetValue(dataType, MyObject.X[i], null);
py.SetValue(dataType, MyObject.Y[i], null);
}
}
Don't know if it helps you any, but you can use reflection to find out what properties (and fields, methods etc.) an object supports at runtime. Check out http://www.csharp-examples.net/reflection-property-names/ for example, if you're interested in learning more.
You can use dynamic objects. You should be careful when targeting fields of a dynamic object since they cannot be checked at compile time. See my example below:
[TestFixture]
public class DynamicObjects
{
[Test]
public void Dynamic_Call()
{
dynamic obj1 = new Apple();
obj1.Weight = 100;
obj1.Color = "Red";
dynamic obj2 = new Orange();
obj2.Weight = 200;
obj2.Width = 10;
Assert.IsTrue(obj1.Weight < obj2.Weight);
}
}
public class Apple
{
public int Weight { get; set; }
public string Color { get; set; }
}
public class Orange
{
public int Weight { get; set; }
public int Width { get; set; }
}