I have a multidimensional array called SensorGetResult. In some cases this array can have a single array in it. If that happens, I must copy this one array and add it again, so I must have 2 arrays now. Then I need to change these array's dateTime property. This is my code:
var helperArray = sensorGet.SensorGetResult[0];
sensorGet.SensorGetResult.Add(helperArray);
sensorGet.SensorGetResult[0].dateTime = end; //It works correctly including this line
sensorGet.SensorGetResult[1].dateTime = start; //At this line both array's dateTime property changes
Why can't I assign dateTime properties to each array individually?
It looks like you are using a reference type for your helperArray.
When the following code executes:
var helperArray = sensorGet.SensorGetResult[0];
sensorGet.SensorGetResult.Add(helperArray);
What actually happens is you take a the first element of SensorGetResult which is a reference to the object (which I believe you intend to copy) and append the reference to the list thus resulting in a list which has two references to the same object in the memory.
If you want it to make a copy of the object, you have to implement that by yourself. Usually this means creating a new object of the same type and copying all the properties.
var objectToCopy = sensorGet.SensorGetResult[0];
var helperArray = new WhatEverTypeIsYourHelperArray {
Property1 = objectToCopy.Property1,
Property2 = objectToCopy.Property2,
// etc.
};
sensorGet.SensorGetResult.Add(helperArray);
But you have to be aware if any of the properties is furthermore a reference type, you need to do this recursively for all the properties.
If WhatEverTypeIsYourHelperArray is type you own, you could utilize Object.MemberwiseClone method and make it all easier for yourself. You can do this by implementing a method like the following. As a note, MemberwiseClone is a protected method hence the need of a new method in your class.
public WhatEverTypeIsYourHelperArray Clone() {
return (WhatEverTypeIsYourHelperArray)this.MemberWiseClone();
}
But even the MemberwiseClone() method doesn't copy reference types for you, rather just copies the pointers to the objects which means that all the properties of reference type of both the original and the cloned object will point to the same objects in the memory.
SensorGetResult row seems to be a reference type.
So when you wrote
var helperArray = sensorGet.SensorGetResult[0];
sensorGet.SensorGetResult.Add(helperArray);
you actually said that new row in SensorGetResult will point to the same object as the first one.
You can implement method like below:
public SensorGetResultRow Clone()
{
return new SensorGetResultRow (this.field1, this.field2, etc...)
//or if you use parameterless constructor
return new SensorGetResultRow ()
{
field1 = this.field1
//etc.
}
}
and use it:
var helperArray = sensorGet.SensorGetResult[0].Clone();
Related
I have a class which has some object fields and I then make a list out of those fields. But I'm confused about how many objects actually exist.
I think the List is a reference type so modifying an object from the list should also modify the field
But if I modify the List & set an item equal to a new object, it only changes the list, not the field
public class MyClass{
public string MyProperty { get; set; } = "Default value";
}
public class TestClass{
private MyClass objectField = new MyClass();
public void run(){
List<MyClass> listOfObjects = new List<MyClass> { objectField };
//Both objectField and listOfObjects[0] have the same value
listOfObjects[0].MyProperty = "Changed value 1st time";
//objectField is "Default value" and listOfObjects[0] is "Changed value 2nd time"
listOfObjects[0].MyProperty = new MyClass();
listOfObjects[0].MyProperty = "Changed value 2nd time";
}
public static void Main() {
TestClass tester = new TestClass();
tester.run();
}
}
Why are there now seemingly 2 objects now? Did the new destroy the reference to the field?
Is there a way to make the field still refer to the new object I create in the list?
This is a simply example, but I have a large list and don't want to have to go and manually update the fields to the values in the list
listOfObjects[0] = new MyClass();
listOfObjects[0].MyProperty = "Changed value 2nd time";
objectField=listOfObjects[0]; //I want to avoid this
Edit (Clarifications)
The real scenario has many object fields of the same base class
I wanted to do the same thing (change property values or instantiate new objects) to all my fields at once so I thought I could just put the fields in a List
I still wanted to be able to refer to the object via it's field name, not just the List index but it appears it can't be done
I'm going to go on a few assumptions here, as your code would not normally compile. Anyways, a generic List<> in C#, when the generic type is a class (reference) type, is a collection of references to those classes, but not a direct pointer to a local object that you put into it.
So basically you have a bunch of class objects hanging out in memory and the list just has references to them. As soon as you new up a new object and REPLACE at one of the indexes, you are replacing the reference to the object in the collection with the new object (not the object in memory itself). The local field still has the reference to the old object.
//E.G.
var objectField = new MyClass();
var listOfObjects = new List<MyClass>{ bjectField };
// Here you are modifying the property of the original object
listOfObjects[0].MyProperty = "1st change";
// Here you are replacing the reference to that object with
// with a reference to a new object, so the properties are totally fresh
// properties go with the old object, a new object has all new fields / properties
// and references
// also, this does not replace the reference to your local field / property
listOfObjects[0] = new MyClass();
// here you are modifying the property on the new object, not the original
listOfObjects[0].MyProperty = "2nd change";
The field, or property belongs to that object. A class in C# is not some structure that you can overlay with a new struct and assume that the fields / properties will just pick up the references to the old fields / properties.
Additionally, passing a local field into a collection does not mean you are giving it a pointer to that field, you are giving it a reference to that object. Even in C++, you would have to specify that you were passing in a pointer to that particular field / property. You cannot do "pointer magic" in C# like you can in C or C++ and change a pointer to a new object (well you can but then you have to do unsafe code, don't do it unless you are interacting with native code structures).
TL;DR; Passing a field into a collection does not mean you are passing a direct pointer to that field into the collection. Therefore, replacing it in the collection does not change your local field.
I have a question about Enumerable.Repeat function.
If I will have a class:
class A
{
//code
}
And I will create an array, of that type objects:
A [] arr = new A[50];
And next, I will want to initialize those objects, calling Enumerable.Repeat:
arr = Enumerable.Repeat(new A(), 50);
Will those objects have the same address in memory?
If I will want to check their hash code, for example in that way:
bool theSameHashCode = questions[0].GetHashCode() == questions[1].GetHashCode();
This will return me true, and if I will change one object properties, all other objects will change it too.
So my question is: is that properly way, to initialize reference type objects? If not, then what is a better way?
Using Enumerable.Repeat this way will initialize only one object and return that object every time when you iterate over the result.
Will those objects have the same address in memory?
There is only one object.
To achieve what you want, you can do this:
Enumerable.Range(1, 50).Select(i => new A()).ToArray();
This will return an array of 50 distinct objects of type A.
By the way, the fact that GetHashCode() returns the same value does not imply that the objects are referentially equal (or simply equal, for that matter). Two non-equal objects can have the same hash code.
Just to help clarify for Camilo, here's some test code that shows the issue at hand:
void Main()
{
var foos = Enumerable.Repeat(new Foo(), 2).ToArray();
foos[0].Name = "Jack";
foos[1].Name = "Jill";
Console.WriteLine(foos[0].Name);
}
public class Foo
{
public string Name;
}
This prints "Jill". Thus it shows that Enumerable.Repeat is only creating one instance of the Foo class.
When using the following code to create an array:
var foos = Enumerable.Repeat(new Foo(), 2).ToArray();
The reason why each location in the array is the same is because you are passing an object, and not a function that creates an object, the code above is the same as:
var foo = new Foo();
var foos = Enumerable.Repeat(foo , 2).ToArray();
The reason above also explains why using a Select statement, like in the code below, creates a new object for each entry, because you are passing a function that dictates how each object is created, rather than the object itself.
Enumerable.Range(1, 2).Select(i => new Foo()).ToArray();
I would use a simple for loop to populate an array with new reference types.
I'm currently freakin out a little.
I'm passing a List to a method of another class and in that class I'm using a different variable name (encapsulated). But here it is:
When I remove an item from the list within the method the item disappears in the other variable as well!
Any suggetions what I'm doing wrong?
Here the code snippet:
public partial class Form1 : Form
{
List<Vector> polygonPoints = new List<Vector>();
private void panel1_Paint(object sender, PaintEventArgs e)
{
// Create Convex Hull of polygon Set
QuickHull qh = new QuickHull();
// here I pass the list to a method in the class QuickHull
// here polygonPoints.Count = 5
List<Vector> hullOfPoints = qh.quickHull(polygonPoints);
// at this point I get polygonPoints.Count = 3
...
}
}
different class QuickHull:
class QuickHull
{
public List<Vector> quickHull(List<Vector> points)
{
List<Vector> convexHull = new List<Vector>();
...
Vector A = points[minPoint];
Vector B = points[maxPoint];
convexHull.Add(A);
convexHull.Add(B);
// at this point 'polygonPoints' also looses these items
points.Remove(A);
points.Remove(B);
...
}
}
I really don't know what to do because this was working all the time but from one moment to the other its not working anymore.
I'd really appreciate every suggetion.
Thanks in advance.
When you pass a List<T> to a method, you're passing a value which contains a reference to that list. That means that the argument you accept in your method, points, is pointing to the same list you instantiated higher up in the call chain.
If you want to pass a reference to a separate list, you'll need to create a new one:
List<Vector> hullOfPoints = qh.quickHull(polygonPoints.ToList());
You can read more on that in "Passing Reference-Type Parameters":
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 pointed to by
the reference, such as the value of a class member. However, you
cannot change the value of the reference itself;
Your problem is that you pass a 'reference' type and then change it. Instead you can create a NEW list (points) to avoid modifications of the previous input list (polygonPoints).
What you see is the expected behaviour.
A List<T> is a reference type, so when you pass it to a method it's the reference to the list that is passed.
Using a different variable name doesn't make it a new list. It's still the same list that you are referencing.
If you want a local copy of the list, you need to create a new list and copy the items into it. You can use the list constructor for this:
List<Vector> local = new List<Vector>(points);
You can also change the type that you send into the method:
public List<Vector> quickHull(IEnumerable<Vector> points)
By using the IEnumerable<T> interface instead of the List<T> class, you limit the use to only enumerating the list. You can still send a list into the method, but you can't change the list by mistake, and you can still use it to create the local copy.
I have method which accepts an object. This object I know is a List<T> however T may vary between children of a base class at any one time when being passed into the method.
So if my base class is MonthType, and I have children called BlockMonthType and AreaMonthType the object passed in could be anyone of List<BlockMonthType> or List<AreaMonthType>.
I want to be able to add items to this object however when I cast it it seems to make a copy and the original object is not updated.
I'm doing this to cast:
var objectList = ((IEnumerable<MonthType>)graphObject.Source.Object).ToList();
Now I want to create a new item and add it to the list
// where ObjectType is a Type variable containing BlockMonthType
var newObject = (BlockMonthType)Activator.CreateInstance(graphObject.Source.ObjectType);
objectList.Add(newObject);
// and carry on the world is good
This works in so far as objectList has a newObject added. However the original variable isn't updated so when I leave the method it's back to it's original state. I know the object is a List<> when passed in as I can see it in the debugger as such.
Is there anyway I can accomplish this?
Here is a cut down version of the method I'm using it in.
public TraverseGraphResult Write(ObjectGraph graphObject)
{
var objectList = ((IEnumerable<MonthType>)graphObject.Source.Object).ToList();
var newObject = (MonthType)Activator.CreateInstance(rule.ObjectType);
newObject.Month = rule.Month;
objectList.Add(newObject);
// Other stuff as well is done but that's the crux of it
}
Hopefully this gives it more context. The method is being used to try and navigate a large object tree with many class types. I'm trying to add a new class type handler which will deal with adding and removing items from a list.
// This is being used in a recursive method to loop down a object's property tree
// .. more code here
// where properties is a List<PropertyInfo>
foreach (var pInfo in properties)
{
if (IsList(pInfo.PropertyType))
{
var enumerable = (IEnumerable)pInfo.GetValue(currentObjectGraph.Source.Object, null);
var sourceEnumerator = enumerable.GetEnumerator();
var graph = new ObjectGraph(enumerable, pInfo.Name);
// this part is made up but essentially the code looks up a list of objects that can deal with this
// particular one and returns it. We then call the write method on that object
var something = GetInterfaceHandlerForObject(enumerable);
something.Write(graph);
}
}
You should make your method generic:
public void MyMethod<T>(List<T> objectList) where T:class, new()
{
objectList.Add(new T());
...
}
Casting is rarely ever necessary when you use generics. Also, your ToList() is causing a new copy of the list to be created.
One drawback to this approach is that T needs to have an empty constructor. If you need to construct an object with parameters you could instead pass in a Func<T>. You can then call it passing in a lambda expression like: (x) => new BlockMonthType(someParameter, orAnother).
I ended up resolving this by storing the underlying List T type in the ObjectGraph object and casting to that when required.
var objectList = ((IEnumerable)graphObject.Source.Object).Cast(monthAllocationRule.ListType);
Without the correct cast objectList was either null or a copy of the list. Now I can add to objectList and know it's added to the source object.
Probably not idea as Ian mentioned above but did the trick.
Is there an easy way to basically just get a copy of the data instead of a reference using this method? I tried .ToArray().Where() but that still seems to pass a reference.
Example:
static void Main(string[] args)
{
List<ob> t = new List<ob>();
t.Add(new ob() { name = "hello" });
t.Add(new ob() { name = "test" });
ob item = t.Where(c => c.name == "hello").First();
// Changing the name of the item changes the original item in the list<>
item.name = "burp";
foreach (ob i in t)
{
Console.WriteLine(i.name);
}
Console.ReadLine();
}
public class ob
{
public string name;
}
You need to create a copy of your ob yourself - it's not something LINQ provides.
You could define a Clone method based on the existing protected MemberwiseClone method:
public class Item
{
public Item Clone()
{
return (Item)this.MemberwiseClone();
}
}
Then:
ob item = t.Where(c => c.name == "hello").First().Clone();
As ob is a class, it is a reference type and therefore any instance of ob, when assigned to a different variable (as is happening within the line ob item = t.Where(c => c.name == "hello").First();) will automatically copy the reference to the original instance, and not copy the actual instance itself. This is a general .NET topic regarding object copying and is separate from LINQ/Lambda,
In order to achieve what you want, you'll need to either create a Shallow Copy or a Deep Copy of the resulting instance from your LINQ projection.
For your ob class, a Shallow Copy would suffice (ShallowCopy generally copies as little as possible whereas DeepCopy copies everything - A good reference for the differences can be found here).
To perform a ShallowCopy of an object, you can use a simply MemberwiseClone which is a built-in method of .NET object type, inherited by all objects.
For something more substantial, you'll have to implement your own DeepCopy function, but that can be relatively simple. Something similar to these implementations as specified here and here.
A much easier way to go is to simply serialize your data to json and then back again. It takes a small performance hit but it is safer as it less error prone and you don't need to modify all your classes.
simply do:
var json = JsonConvert.SerializeObject(myObject)
var clone = JsonConvert.DeserializeObject<T>(json)