Cloning Complex Object having DataTable and other object as property in C# - c#

I want to deep copy of my complex C# object having DataTable as property.It is throwing error as "Table SalesData does not belong to this DataSet."
Here is my C# object:
public class Foo
{
public VehicleDetails VehicleDetails { get; set; }
public VehicleCondition VehicleCondition { get; set; }
public string Zipcode { get; set; }
public string StateCode { get; set; }
public DataTable SalesData { get; set; }
public DataTable OtherDataTable { get; set; }
}
I have used following code to clone object:
public static T CloneFullObject<T>(T i)
{
if (Object.ReferenceEquals(i, null)) return default(T);
var x = new XmlSerializer(i.GetType());
using (var m = new MemoryStream())
{
x.Serialize(m, i);
m.Seek(0, SeekOrigin.Begin);
return (T)x.Deserialize(m);
}
}
I am creating object as:
Foo foo = new Foo();
VehicleDetails vehicleDetail = new VehicleDetails();
// Fill vehicleDetail object
VehicleCondition vehicleCondition = new VehicleCondition ();
// Fill vehicleCondition object
foo.VehicleDetails = vehicleDetail;
foo.VehicleCondition = vehicleCondition;
DataTable salesData = getDataTable();
salesData.TableName = "salesData";
foo.SalesData = salesData;
DataTable otherData = getDataTable();
salesData.TableName = "otherData";
foo.OtherDataTable = salesData;
Below code is throwing error as:
System.InvalidOperationException: There was an error generating the XML document. ---> System.ArgumentException: Table salesData does not belong to this DataSet.
Foo clonefullObject = CloneFullObject(foo);
Please help if I am missing something before cloning object.
Note: both Datatable have value and it's not null.
Edit:
class Foo have some complex properties like:
private int _mileage;
public void SetMileage(int mileage) { _mileage = mileage; }
private int _expectedMileage;
public void SetExpectedMileage(int mileage) { _expectedMileage = mileage; }
public int GetMileage(bool flag)
{
return (flag)
? _mileage
: Math.Max(_mileage, _expectedMileage);
}

When cloning/copying objects you can simply clone/copy them. Serializing is quite expensive overkill, especially when implemented without concern for performance (you can read more in this SE question).
However, serialization is not your real problem, copying DataTable with both structure and data is. And it appears that your problem is not really your problem, your approach to it is your problem. DataTable.Copy() does all of that.
So how to do it? Well, how about properly?
On of proper approaches would be to implement ICloneable interface. It is kind of clumsy as return type is object. By implementing it on sub classes you can chain it deeper. I used as for casting in sample, have in mind that it will not generate exception on type mismatch. (As usually you can read more on some old SE question). You might, or might not, want to generate exceptions on faulty state (null) of DataTables.
public class Foo : ICloneable
{
//some fields....
private int _Bar; //private field
public void SetBar(int value) { _Bar = value; } //Field setter
public object Clone()
{
var result = new Foo()
{
_Bar = _Bar, // private members are accessible from their scope, even when object is different
Zipcode = Zipcode,
StateCode = StateCode,
SalesData = SalesData== null ? null : SalesData.Copy(),
OtherDataTable = OtherDataTable == null ? null : OtherDataTable.Copy(),
VehicleDetails = VehicleDetails.Clone() as VehicleDetails,
VehicleCondition = VehicleCondition.Clone() as VehicleCondition,
};
// alternatively you can call setter methods
result.SetBar(_Bar);
return result;
}
}
Notes:
You should work on creating object in better style, for example using Factory or at least use Object Initializer or constructor.
#SergeyBerezovskiy made a valid point by suggesting data model classes instead of full data tables.

Related

Why is my game serializing this class?

So I'm making a game, and it saves users' progress on the computer in a binary file. The User class stores a few things:
Integers for stat values (Serializable)
Strings for the Username and the skin assets
Lists of both the Achievement class and the InventoryItem class, which I have created myself.
Here are the User fields:
public string Username = "";
// ID is used for local identification, as usernames can be changed.
public int ID;
public int Coins = 0;
public List<Achievement> AchievementsCompleted = new List<Achievement>();
public List<InventoryItem> Inventory = new List<InventoryItem>();
public List<string> Skins = new List<string>();
public string CurrentSkinAsset { get; set; }
The Achievement class stores ints, bools, and strings, which are all serializable. The InventoryItem class stores its name (a string) and an InventoryAction, which is a delegate that is called when the item is used.
These are the Achievement class's fields:
public int ID = 0;
public string Name = "";
public bool Earned = false;
public string Description = "";
public string Image;
public AchievmentDifficulty Difficulty;
public int CoinsOnCompletion = 0;
public AchievementMethod OnCompletion;
public AchievementCriteria CompletionCriteria;
public bool Completed = false;
And here are the fields for the InventoryItem class:
InventoryAction actionWhenUsed;
public string Name;
public string AssetName;
The source of the InventoryAction variables are in my XNAGame class. What I mean by this is that the XNAGame class has a method called "UseSword()" or whatever, which it passes into the InventoryItem class. Previously, the methods were stored in the Game1 class, but the Game class, which Game1 inherits from, is not serializable, and there's no way for me to control that. This is why I have an XNAGame class.
I get an error when trying to serialize: "The 'SpriteFont' class is not marked as serializable", or something like that. Well, there is a SpriteFont object in my XNAGame class, and some quick tests showed that this is the source of the issue. Well, I have no control over whether or not the SpriteFont class is Serializable.
Why is the game doing this? Why must all the fields in the XNAGame class be serializable, when all I need is a few methods?
Keep in mind when answering that I'm 13, and may not understand all the terms you're using. If you need any code samples, I'll be glad to provide them for you. Thanks in advance!
EDIT: One solution I have thought of is to store the InventoryAction delegates in a Dictionary, except that this will be a pain and isn't very good programming practice. If this is the only way, I'll accept it, though (Honestly at this point I think this is the best solution).
EDIT 2: Here's the code for the User.Serialize method (I know what I'm doing in inefficient, and I should use a database, blah, blah, blah. I'm fine with what I'm doing now, so bear with me.):
FileStream fileStream = null;
List<User> users;
BinaryFormatter binaryFormatter = new BinaryFormatter();
try
{
if (File.Exists(FILE_PATH) && !IsFileLocked(FILE_PATH))
{
fileStream = File.Open(FILE_PATH, FileMode.Open);
users = (List<User>)binaryFormatter.Deserialize(fileStream);
}
else
{
fileStream = File.Create(FILE_PATH);
users = new List<User>();
}
for (int i = 0; i < users.Count; i++)
{
if (users[i].ID == this.ID)
{
users.Remove(users[i]);
}
}
foreach (Achievement a in AchievementsCompleted)
{
if (a.CompletionCriteria != null)
{
a.CompletionCriteria = null;
}
if (a.OnCompletion != null)
{
a.OnCompletion = null;
}
}
users.Add(this);
fileStream.Position = 0;
binaryFormatter.Serialize(fileStream, users);
You cannot serialize a SpriteFont by design, actually this is possible (.XNB file) but it hasn't been made public.
Solution:
Strip it off your serialized class.
Alternatives:
If for some reasons you must serialize some font, the first thing that comes to my mind would be to roll-out your own font system such as BMFont but that's a daunting task since you'll have to use it everywhere else where you might already do ...
Generate a pre-defined amount of fonts (i.e. Arial/Times/Courier at size 10/11/12 etc ...) using XNA Content app (can't recall its exact name); then store this user preference as two strings. With a string.Format(...) you should be able to load the right font back quite easily.
Alternative 2 is certainly the easiest and won't take more than a few minutes to roll-out.
EDIT
Basically, instead of saving a delegate I do the following:
inventory items have their own type
each type name is de/serialized accordingly
their logic does not happen in the main game class anymore
you don't have to manually match item type / action method
So while you'll end up with more classes, you have concerns separated and you can keep your main loop clean and relatively generic.
Code:
public static class Demo
{
public static void DemoCode()
{
// create new profile
var profile = new UserProfile
{
Name = "Bill",
Gold = 1000000,
Achievements = new List<Achievement>(new[]
{
Achievement.Warrior
}),
Inventory = new Inventory(new[]
{
new FireSpell()
})
};
// save it
using (var stream = File.Create("profile.bin"))
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, profile);
}
// load it
using (var stream = File.OpenRead("profile.bin"))
{
var formatter = new BinaryFormatter();
var deserialize = formatter.Deserialize(stream);
var userProfile = (UserProfile) deserialize;
// set everything on fire :)
var fireSpell = userProfile.Inventory.Items.OfType<FireSpell>().FirstOrDefault();
if (fireSpell != null) fireSpell.Execute("whatever");
}
}
}
[Serializable]
public sealed class UserProfile
{
public string Name { get; set; }
public int Gold { get; set; }
public List<Achievement> Achievements { get; set; }
public Inventory Inventory { get; set; }
}
public enum Achievement
{
Warrior
}
[Serializable]
public sealed class Inventory : ISerializable
{
public Inventory() // for serialization
{
}
public Inventory(SerializationInfo info, StreamingContext context) // for serialization
{
var value = (string) info.GetValue("Items", typeof(string));
var strings = value.Split(';');
var items = strings.Select(s =>
{
var type = Type.GetType(s);
if (type == null) throw new ArgumentNullException(nameof(type));
var instance = Activator.CreateInstance(type);
var item = instance as InventoryItem;
return item;
}).ToArray();
Items = new List<InventoryItem>(items);
}
public Inventory(IEnumerable<InventoryItem> items)
{
if (items == null) throw new ArgumentNullException(nameof(items));
Items = new List<InventoryItem>(items);
}
public List<InventoryItem> Items { get; }
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
var strings = Items.Select(s => s.GetType().AssemblyQualifiedName).ToArray();
var value = string.Join(";", strings);
info.AddValue("Items", value);
}
#endregion
}
public abstract class InventoryItem
{
public abstract void Execute(params object[] objects);
}
public abstract class Spell : InventoryItem
{
}
public sealed class FireSpell : Spell
{
public override void Execute(params object[] objects)
{
// using 'params object[]' a simple and generic way to pass things if any, i.e.
// var world = objects[0];
// var strength = objects[1];
// now do something with these !
}
}
Okay, so I figured it out.
The best solution was to use a Dictionary in the XNAGame class, which stores two things: an ItemType (an enumeration), and an InventoryAction. Basically, when I use an item, I check it's type and then look up it's method. Thanks to everyone who tried, and I'm sorry if the question was confusing.

Looping to Create and Add New Objects to ArrayList

Edit to save you from reading through this whole post
tldr: an object's fields should not be static unless you want all instances of that object to have the same value for that field
I'm trying to create and populate an ArrayList of Blog objects. I do know the generic way do this:
create ArrayList of Blogs
loop (some condition)
create new Blog
add this Blog to AL
However, when I attempt to do so within the while(datareader.read()) loop, all of the elements in the ArrayList are exactly the same Blog. Specifically, I end up with an ArrayList filled with multiple pointers to the very last Blog object from the database table. Here is my code:
public static ArrayList AllBlogs()
{
SqlDataReader dr = anonPage.ExecuteReader("SELECT * FROM Kristina_Blogs");
ArrayList allBlogs = new ArrayList();
if (dr.HasRows)
{
while (dr.Read())
{
Blog b = new Blog();
//grab a row from Kristina_Blogs and assign those attributes to b
b.setTitle(dr["title"].ToString());
b.setMessage(dr["message"].ToString());
b.setId(dr["id"]);
allBlogs.Add(b);
}
}
dr.Close();
return allBlogs;
}
As I said before, the result of this is an ArrayList filled with pointers to the very last blog from the Kristina_Blogs table. I imagine the ArrayList allBlogs looks like [b, b, b, ... b] and therefore they ALL get updated when I say b.setTitle() etc. But how can this be the case if I am creating a NEW Blog object at the beginning of each iteration?
Here is some extra info that you don't have to read but it might clear up some confusion about the structure of the problem:
Blog object has id, title, and message fields and their respective getter/setters
Kristina_Blogs is a table representing these blogs with columns for id, title, message
The suggestions say to include a tag for my DB engine but I can't find a tag for it: Microsoft SQL Server Management Studio
This code works perfectly when I use an ArrayList of Strings instead of Blogs
Edit: Including the code from Blog class
public class Blog
{
public App myApp;
public static string Title;
public static string Message;
public static int Id;
//constructors
public Blog() { }
public Blog(App App) { this.myApp = App; }
//all getters and setters look like this
public string getTitle() { return Title; }
public void setTitle(string t) { Title = t; }
}
The main problem you have, as I mentioned in comments is your member variables are static, so when you set the value, they change in all instances. you should change your code this way:
public class Blog
{
public int Id { get; set; }
public string Title { get; set; }
public string Message { get; set; }
}
And fill your list this way, don't forget to add using System.Linq;:
var result = new List<Blog>();
var connection = #"your connection string";
var command = "SELECT * FROM Kristina_Blogs";
var adapter = new System.Data.SqlClient.SqlDataAdapter(command, connection);
var dataTable = new DataTable();
//Get data
adapter.Fill(dataTable);
dataTable.Rows.Cast<DataRow>().ToList()
.ForEach(row =>
{
var b = new Blog();
b.Id = row.Field<int>("Id");
b.Title = row.Field<string>("Title");
b.Message = row.Field<string>("Message");
result.Add(b);
});
return result;
Note:
When you create a member static, it is shared between all instances of that calss.
In C# you can use property to get or set values, you don't need to setX or setY, when you get the value of a property, the get code of that property will execute and when you assign a value to a property the set part of it will execute. you can define properties this way:
Property:
private int id;
public int Id
{
get
{
return id;
}
set
{
id = value;
}
}
or more simple:
public int Id { get; set; }
All of the fields in your Blog class are static, meaning they're shared between all object instances. You want them to be instance field (meaning not static) so that each object has its own copy of each of those values.
Remove the static attributes from your class:
public class Blog
{
public App myApp;
public String Title;
public String Message;
public int Id;
//constructors
public Blog() { }
public Blog(App App) { this.myApp = App; }
//all getters and setters look like this
public String getTitle() { return Title; }
public String getMessage() { return Message; }
public void setTitle(String t) { Title = t; }
public void setMessage(String m) { Message = m; }
}
When you use static variables, all instances of an object will contain the same values in those variables. By removing the static keyword, you are allowing different instances of the object to hold different values.
Now, every time you create a blog object, that object's Title and Message etc, will contain its own information.
I would make a quick method to prevent null value from throwing error
public static string GetSafeString(SqlDataReader reader, int index)
{
if (!reader.IsDBNull(index))
return reader.GetString(index);
else
return string.Empty;
}
Replace this code:
while (dr.Read())
{
Blog b = new Blog();
//grab a row from Kristina_Blogs and assign those attributes to b
b.setTitle(dr["title"].ToString());
b.setMessage(dr["message"].ToString());
b.setId(dr["id"]);
allBlogs.Add(b);
}
With This Code:
while (dr.Read())
{
Blog b = new Blog();
//grab a row from Kristina_Blogs and assign those attributes to b
b.setId(dr.GetInt32(0));
b.setTitle(GetSafeString(dr, 1);
b.setMessage(GetSafeString(dr, 2);
allBlogs.Add(b);
}
Where the number is the index of field in the record and assuming "id" is an integer. Also consider moving creation of "Blog" object outside of loop and just change values.

Preserve value of an object instance

I think this problem is a value/reference instantiate problem in Object Oriented languages like C#. But I'm newbie and I don't know how to turnaround.
I have a method with this piece of code:
List<CSpropr> listActionpropr = CSpropr.searchList(actionCondition); // Get a list of all records of table PROPR for the 'actioncondition' specified
// For each record...
foreach (CSpropr instance in listActionpropr)
{
instance.ValName = "John";
instance.ValPhone = 323234232;
instance.update(); // This make and UPDATE of the record in DB
}
Later in the same method, I want to use the first version of listActionpropr, before the update. To make a sort of rollback. But when I iterate the listActionpropr variable I get the list of records with the changes in Name and Phone values.
For example:
foreach (CSApropr instance1 in listActionpropr )
{
instance1.update();
}
Is there an elegantly way to preserve the value without create a new search to other variable? Like this:
List<CSpropr> listActionpropr = CSpropr.searchList(actionCondition); // Get a list of all records of table PROPR for the 'actioncondition' specified
List<CSpropr> preservedList = CSpropr.searchList(actionCondition); // Get a list of all records of table PROPR for the 'actioncondition' specified
// For each record...
foreach (CSpropr instance in listActionpropr)
{
instance.ValName = "John";
instance.ValPhone = 323234232;
instance.update(); // This make and UPDATE of the record in DB
}
....
foreach (CSApropr instance1 in preservedList )
{
instance1.update();
}
I would use DeepClone in this case with serialization as per this answer : https://stackoverflow.com/a/519512/841467
My fast implementation in LinqPad was :
void Main()
{
List<CSpropr> listActionpropr = CSpropr.searchList("act"); // Get a list of all records of table PROPR for the 'actioncondition' specified
List<CSpropr> preservedList = listActionpropr.DeepCopy(); // Get a list of all records of table PROPR for the 'actioncondition' specified
// For each record...
foreach (CSpropr instance in listActionpropr)
{
instance.ValName = "John";
instance.ValPhone = 323234232;
instance.update(); // This make and UPDATE of the record in DB
}
foreach (CSpropr instance1 in preservedList )
{
instance1.update();
}
}
// Define other methods and classes here
[Serializable]
public class CSpropr {
public string ValName {get;set;}
public int ValPhone {get;set;}
public void update() {
ValName.Dump();
ValPhone.Dump();
}
public static List<CSpropr> searchList(string act) {
return new List<CSpropr> { new CSpropr {ValName = "First", ValPhone = 4444} , new CSpropr {ValName = "First", ValPhone = 4444 }};
}
}
public static class GenericCopier
{
public static T DeepCopy<T>(this T original) where T : class
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, original);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T)binaryFormatter.Deserialize(memoryStream);
}
}
}
result is :
John
323234232
John
323234232
First
4444
First
4444
What you are talking about is close to transactions in c# objects. There is no inherent support for object transactions in c#.
Microsoft did start a project on STM (Software Transactional Memory) to support the transaction features for .NET objects. However, the project has already been retired for various reasons.
So, all you can do now is have a separate object for original list.
You could either DeepCopy the object with the some helper method as described in other answer or implement IClonable interface for you object, which does the deep copy for you. An example would be :
public class Person : ICloneable
{
public string LastName { get; set; }
public string FirstName { get; set; }
public Address PersonAddress { get; set; }
public object Clone()
{
Person newPerson = (Person)this.MemberwiseClone();
newPerson.PersonAddress = (Address)this.PersonAddress.Clone();
return newPerson;
}
}
The Address class uses MemberwiseClone to make a copy of itself.
public class Address : ICloneable
{
public int HouseNumber { get; set; }
public string StreetName { get; set; }
public object Clone()
{
return this.MemberwiseClone();
}
}
Cloning a Person:
Person herClone = (Person)emilyBronte.Clone();
The example is taken from one of the excellent blogs in internet for c#
Of course, when I meant object, it's applicable to List<objects> as well.
Basically you want two identical lists but if you change one element of the list1 it should not affect the same element of list2 .
You can do this in various ways: eg using serialization.
Following code will show my way:
Module Module1
Sub Main()
Dim listone As New List(Of demo) 'first list
Dim listwo As New List(Of demo) ' second list
Dim var1 As Integer
Dim var2 As String
Dim obj As New demo() 'first object created in list-1
obj.id = 1
obj.name = "sumi"
listone.Add(obj) 'first object inserted in the list-1
Dim obj1 As New demo()
obj1.id = 3
obj1.name = "more"
listone.Add(obj1) 'Second object inserted in the list-1
For Each w In listone
Dim obj3 As New demo()
var1 = w.id
obj3.id = var1
var2 = w.name
obj3.name = var2
listwo.Add(obj3) 'looping all objects of list-1 and adding them in list-2 .Hence making both lists identical
Next
For Each p In listone 'making change in the list-1 and this change should not be refelected in list-2
If (p.id = 1) Then
p.id = 5
End If
Next
For Each z In listone
Console.WriteLine(z.id)
Console.WriteLine(z.name)
Next
For Each q In listwo
Console.WriteLine(q.id)
Console.WriteLine(q.name)
Next
Console.ReadLine()
End Sub
Class demo
Public name As String
Public id As Integer
End Class
End Module
Output:
5
sumi
3
more
1
sumi
3
more
Hence cloned list remains unaffected irrespective of changes in original list
You can store your first list object like that:
List<CSpropr> StoreFirstListInTemp(List<CSpropr> listActionpropr)
{
List<CSpropr> temp = new List<CSpropr>();
temp.AddRange(listActionpropr);
return temp;
}

Cannot deserialize XML into a list using XML Deserializer

This follows on from my previous question Serialize list of interfaces using XML Serialization
public class MeterWalkOrder
{
public MeterWalkOrder()
{
Meters = new List<IMeter>();
}
public String Name { get; set; }
[XmlIgnore]
public List<IMeter> Meters { get; set; }
[XmlArrayItem(ElementName = "Meter")]
[XmlArray(ElementName = "Meters")]
public List<Meter> SerializableMeters
{
get
{
return Meters.Cast<Meter>().ToList();
}
set
{
Meters = new List<IMeter>(value);
}
}
}
public interface IMeter {
int MeterID { get; set; }
}
public class Meter : IMeter {
public int MeterID { get; set; }
public string SerialNumber { get; set; }
}
}
I am using the extension method below to deserialize the XML back into my object (ideally I would prefer the extension method to be off of object, but I not too comfortable with extension methods so I have left like this for now)...
public static class SerializationExtensions
{
public static T LoadFromXML<T>(this string xmlString)
{
T returnValue = default(T);
XmlSerializer serial = new XmlSerializer(typeof(T));
StringReader reader = new StringReader(xmlString);
object result = serial.Deserialize(reader);
if (result != null && result is T)
{
returnValue = ((T)result);
}
reader.Close();
return returnValue;
}
....However, when I give the XML below....
<?xml version="1.0"?>
<MeterWalkOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Red Route</Name>
<Meters>
<Meter>
<MeterID>1</MeterID>
<SerialNumber>12345</SerialNumber>
</Meter>
<Meter>
<MeterID>2</MeterID>
<SerialNumber>SE</SerialNumber>
</Meter>
</Meters>
</MeterWalkOrder>
No meters are populated?
Does anyone know what could cause this problem? The XML is valid and SerializeableMeters is simply a property that reads from and writes to Meters but casting it as a concrete class due to the known issues with using interfaces in serialization
The problem is that XmlSerializer deserializes a property referring to a class implementing IList<T> in the following way:
It calls the getter to get the list. If null, it allocates a list and sets it via the setter. It holds onto the list in some local variable while reading it.
It deserializes each list element, and adds it to the list it is holding.
And that's it. It never calls the containing class's list property setter afterwards.
You can verify this by replacing your List<Meter> with an ObservableCollection<Meter>, and setting a debug listener for when the collection changes:
[XmlArrayItem(ElementName = "Meter")]
[XmlArray(ElementName = "Meters")]
public ObservableCollection<Meter> SerializableMeters
{
get
{
Debug.WriteLine("Returning proxy SerializableMeters");
var list = new ObservableCollection<Meter>(Meters.Cast<Meter>());
list.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(list_CollectionChanged);
return list;
}
set
{
Debug.WriteLine("Setting proxy SerializableMeters");
Meters = new List<IMeter>(value.Cast<IMeter>());
}
}
static void list_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
var collection = (IList<Meter>)sender;
Debug.WriteLine("Proxy collection changed to include : ");
foreach (var item in collection)
Debug.WriteLine(" " + item.ToString());
}
Doing so, you'll see the following debug output:
Returning proxy SerializableMeters
Returning proxy SerializableMeters
Returning proxy SerializableMeters
Returning proxy SerializableMeters
Proxy collection changed to include :
Meter: 1, 12345
Proxy collection changed to include :
Meter: 1, 12345
Meter: 2, SE
As you can see, the list is never set back.
Luckily, there's an easy alternative. If you return a proxy array instead of a proxy List, XmlSerializer will allocate the array itself, populate it, and set it via the setter -- which is just what you want!
[XmlArrayItem(ElementName = "Meter")]
[XmlArray(ElementName = "Meters")]
public Meter [] SerializableMeters
{
get
{
return Meters.Cast<Meter>().ToArray();
}
set
{
Meters = new List<IMeter>(value.Cast<IMeter>());
}
}
And then later
var meters = xml.LoadFromXML<MeterWalkOrder>();
Debug.Assert(meters.Meters.Count == 2); // No assert.

C# Object Initializer : Set Property from another one

I have the following object where in my constructor I add a new Guid as the Id.
public class MyObject
{
public MyObject()
{
Id = Guid.NewGuid().ToString();
}
public String Id { get; set; }
public String Test { get; set; }
}
I want to do something like that in an object initializer :
var obj = new MyObject
{
Test = Id; // Get new GUID created in constructor
}
Is it possible?
No, you can't do that. You'd have to just set it in a separate statement:
var obj = new MyObject();
obj.Test = obj.Id;
The right-hand side of the property in an object initializer is just a normal expression, with no inherent connection to the object being initialized.
If this is something you regularly want to do with one specific type, you could add a method:
public MyObject CopyIdToTest()
{
this.Test = Id;
return this;
}
and then use:
MyObject obj = new MyObject().CopyIdToTest();
or with other properties:
MyObject obj = new MyObject
{
// Set other properties here
}.CopyIdToTest();
No -- you can't access an object's properties inside an initializer. The initializer is basically some syntactic sugar for programmers.
Consider situations like:
class Program
{
static void Main()
{
var Id = "hello";
var obj = new MyObject
{
Test = Id // Get new GUID created in constructor
};
}
}
The Id you'd assign (if your idea was valid, which again, it isn't) isn't necessarily the Id you'd be getting.

Categories

Resources