I have this code below where I fetch a object 'yogaSpaceEvent' from the DB as a entity.
I want to save the values before I modify anything and then pass the old values (as a object) and the object with the newly saved values to another method 'getEditedEventEmail' to do some logic. But the 'oldEvent' object has all the new values of 'newEvent'.
YogaSpaceEvent yogaSpaceEvent = yogaSpace.YogaSpaceEvents.Where(k => k.YogaSpaceEventId == details.EventId).First();
if (yogaSpaceEvent == null)
throw new Exception("You Don't Have A Yoga Space Event With This ID!");
YogaSpaceEvent oldEvent = yogaSpaceEvent; // save all the old values in this object
var displayedTime = details.StartTime.GetType().GetMember(details.StartTime.ToString()).First().GetCustomAttribute<DisplayAttribute>().Name;
yogaSpaceEvent.EventDateTime = details.EventDate.AddHours(DateTime.Parse(displayedTime).Hour).AddMinutes(DateTime.Parse(displayedTime).Minute);
yogaSpaceEvent.Time = details.StartTime;
yogaSpaceEvent.Duration = details.Duration;
yogaSpaceEvent.Style = details.Style;
yogaSpaceEvent.DateUpdated = DateTime.Now;
YogaSpaceEvent newEvent = yogaSpaceEvent; // save all the new values in this object to compare with the old object
Tuple<string, string> editedEventEmail = _emailingService.GetEditedEventEmail(oldEvent, newEvent);
Assuming YogaSpaceEvent is a reference type:
YogaSpaceEvent oldEvent = yogaSpaceEvent;
Just copies off the reference. Both references still point to the same object. If after that line you had written:
yogaSpaceEvent = new YogaSpaceEvent();
Then you would get the behavior you are looking for as the two variables would be pointing at different objects. You can also do a "deep copy" into oldevent but the techniques for doing so in the general case are a bit more involved (for simple cases, MemberwiseClone is fine).
The general case of deep copying is is discussed further in: How do you do a deep copy of an object in .NET (C# specifically)?
Please test this:
YogaSpaceEvent oldEvent = new YogaSpaceEvent{
property1 = yogaSpaceEvent.property1,
property2 = yogaSpaceEvent.property2,
.
.
.
};
Related
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();
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 this part of code:
MeasDataSet dset = new MeasDataSet();
MeasDataPoint point = new MeasDataPoint();
//...
MeasDataSet dset2 = new MeasDataSet(dset._path);
dset2.SaveResults();
point = dset2.GetDataPointAt(dset2.Size - 1);
point.Current = 7566;
dset2.SaveResults();
Where MeasDataPoint and Set are just some classes containing measurement data (point a single point and set a collection of points with additional methods)
When calling SaveResults() it should save the data inside the DataSet to a file, but using the code above doesn't save the old point but instead the altered one (point.Current = 7566;).
So basically point now changes my values inside my data set instead of being a copy what i expected it to be.
GetDataPointAt (method of DatasSet):
public MeasDataPoint GetDataPointAt(int numberOfPoint)
{
return _dataPoints.ElementAt(numberOfPoint);
}
Anyone an idea why it behaves this way?
Is dset._path the entire collection of MeasDataPoint objects? If so, it appears the constructor new MeasDataSet(dset._path) is simply internally storing a reference to your original dset collection of points which means it is manipulating your original collection. See Passing Parameters (C# Programming Guide).
You could extend or modify the MeasDataSet constructor to iterate over the MeasDataPoint objects to create new ones by deep copying when assigning to dset2. Here is a rough idea:
public MeasDataSet(List<MeasDataPoint> path) {
var copiedPath = new List<MeasDataPoint>();
foreach(var point in path) {
copiedPath.Add(new MeasDataPoint() {
Prop1 = point.Prop1,
Prop2 = point.Prop2
});
}
// do something with your newly created deep copy of copiedPath...
}
I have appSourceInfoModel taken from Database, now i am passing ViewModel i.e. reviewerAppsBacklogViewModel and if reviewerAppsBacklogViewModel and appSourceInfoModel are same then do not update database if there are changes then Update. I am doing, first assigning to variable var DBappSourceInfoModel = appSourceInfoModel; then I assigning some values to appSourceInfoModel then comparing the initially saved model DBappSourceInfoModel and appSourceInfoModel. But, assigning some values to appSourceInfoModel also change values in the initially saved model DBappSourceInfoModel. All of the code can be found below.
AppSourceInfo appSourceInfoModel = _appSourceInfoRepository.Get(a => a.Review.ReviewId == reviewId);
var DBappSourceInfoModel = appSourceInfoModel; //Initially save Model in var
appSourceInfoModel.Cost = reviewerAppsBacklogViewModel.Cost;
appSourceInfoModel.InProgress = true;
appSourceInfoModel.PegiRating = reviewerAppsBacklogViewModel.PegiRating;
appSourceInfoModel.Rating = reviewerAppsBacklogViewModel.AverageUserReviewsRating;
appSourceInfoModel.DownloadCounter = reviewerAppsBacklogViewModel.NoofDownloadsFromSource;
appSourceInfoModel.ReviewCounter = reviewerAppsBacklogViewModel.NoofReviewOfSource;
appSourceInfoModel.StoreCategory = reviewerAppsBacklogViewModel.StoreCategory;
var IsAppSourceInfoModelChanged = !DBappSourceInfoModel.Equals(appSourceInfoModel);
if (IsAppSourceInfoModelChanged)
{
_appSourceInfoRepository.Update(appSourceInfoModel);
}
I have Solved it using this simple Code in My Model i.e. AppSourceInfo
public object Clone()
{
return this.MemberwiseClone();
}
and change the following code
var DBappSourceInfoModel = appSourceInfoModel; //Initially save Model in var
to
var DBappSourceInfoModel = (AppSourceInfo) appSourceInfoModel.Clone();
You need to perform a Copy (shallow probably sufficient)
var DBappSourceInfoModel = appSourceInfoModel;
Is simply creating a reference to the same object. Implement ICloneable on the DBappSourceInfoModel then use Clone method,
Your Clone method needs to copy all the info to the new Object, also performing Deep Copies on internal references if needed,
This will copy all the details to the other object and create two separate objects,
look here for reference:
https://msdn.microsoft.com/en-us/library/system.icloneable%28v=vs.110%29.aspx
EDIT
Just to be clear, you also need to use the IComparable interface to define how the objects are compared for equality,
https://msdn.microsoft.com/en-us/library/system.icomparable%28v=vs.110%29.aspx
please help me understand what's going on in here and whether it should work like that ?
I have a generic list of objects from a CMS:
For example List<MyCMS.Articles.Article> myArticles = articles.All;
Later I output the contents of the list in a JSON format (for CMS UI - table list).
Now a single record would include:
article.Title
article.Alias
article.Guid
article.Description
+
article.SeoProperties.TitleOverride
article.SeoProperties.H1Tag
article.StateProperties.IsActive
article.StateProperties.Channels
etc...
as you can see an Article object has an additional class property - with common properties (used on other object types in the CMS)
I also use a filter class that does some filter operations with LINQ on the collection to return me only articles within a certain channel, for example...
So the problem is that when I serialize the collection as JSON - there are only a few "columns" that I really need to display in my table list, while I have no need in other fields - especially, possibly long fields such as "description" (lazy loaded from file system), etc... - I serialize with DataContractJsonSerializer...
I need a way to control what fields will be included in the JSON result... What I do is I use reflection to set property values to null if I don't need the property and
decorate class properties with [DataMember(IsRequired = false, EmitDefaultValue = false)] attributes... - it should work well - but - as soon as I go over (even cloned!!) collection of final objects to strip off the fields = set value to "null" - property value becomes null - application wide - in all collections of such objects... eh ?
Some demo code in here:
void Page_Load() {
MyCms.Content.Games games = new MyCms.Content.Games();
List<MyCms.Content.Games.Game> allGames = games.All;
MyCms.Content.Games games2 = new MyCms.Content.Games();
List<MyCms.Content.Games.Game> allGamesOther = games2.All;
Response.Write("Total games: " + allGames.Count + "<br />");
//This is our fields stripper - with result assigned to a new list
List<MyCms.Content.Games.Game> completelyUnrelatedOtherIsolated_but_notSureList = RemoveUnusedFields(allGamesOther);
List<MyCms.Content.Games.Game> gamesFiltered = allGames.Where(g=>g.GamingProperties.Software=="89070ef9-e115-4907-9996-6421e6013993").ToList();
Response.Write("Filtered games: " + gamesFiltered.Count + "<br /><br />");
}
private List<MyCms.Content.Games.Game> RemoveUnusedFields(List<MyCms.Content.Games.Game> games)
{
List<MyCms.Content.Games.Game> result = new List<MyCms.Content.Games.Game>();
if (games != null && games.Count > 0)
{
//Retrieve a list of current object properties
List<string> myContentProperties = MyCms.Utils.GetContentProperties(games[0]);
MyCms.PropertyReflector pF = new MyCms.PropertyReflector();
foreach (MyCms.Content.Games.Game contentItem in games)
{
MyCms.Content.Games.Game myNewGame = (MyCms.Content.Games.Game)contentItem.Clone();
myNewGame.Images = "wtf!"; //just to be sure we do set this stuff not only null
pF.SetValue(myNewGame, "GamingProperties.Software", ""); //set one property to null for testing
result.Add(myNewGame);
}
}
return result;
}
Objects are set to their "Default values" (basically, null, in most cases) with this:
private object GetDefaultValue(Type type)
{
if (type.IsValueType)
{
try
{
return Activator.CreateInstance(type);
}
catch {
return null;
}
}
return null;
}
Quite probably your having trouble with differentiating between a shallow copy and a deep copy.
If a field is a value type, a bit-by-bit copy of the field is performed. If a field is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object.
With a deep copy when you clone an object and that object has a field of reference type, a new clone of that object is created and assigned to the field (vs just referencing the first object). Thus you have to completely different objects that share nothing.
That means if you're using clone and some of the properties are in fact subproperties (that is, properties of an instance inside the original object) you're changing it application wide, because you're acting upon a reference, not a new instance of the subobject.
You have more information about it in
http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx
You could create a kind of modelview classes with the necessary fields and use something like Automapper to fill them. By this way, you will have a nice code, easy to maintain and highly customizable.