public struct volt_struct
{
public string volt1;
public string volt2;
public string volt2;
public string volt3;
}
private class Injection_class
{
public volt_struct stru1;
public volt_struct stru2;
public volt_struct stru3;
public volt_struct stru4;
public volt_struct stru5;
public volt_struct stru6;
}
public void main()
{
Injection_class Time = new Injection_class();
//Here is code that fills Time with Time values as string type
string s="";
FieldInfo[] fi_inner = Time.stru1.GetType().GetFields();
FieldInfo[] fi_outer = Time.GetType().GetFields();
// This part is wrong, but shows what I want to achive.
foreach(FieldInfo field_outer in fi_outer)
{
foreach(FieldInfo field_inner in fi_inner)
{
s = string.concat(s+field_outer.field_inner.GetValue(Time) + ";");
}
}
}
I want to concatenate the strings stored inside Time into the string s using reflection. Later on I have to modify the class and struct and I don't want to adjust the concatenating code.
I got the results I want with using a foreach loop for each struct inside the class.
foreach (FieldInfo field in fi_inner)
{
s = string.Concat(s + field.GetValue(Time.stru1) + ";");
//field.SetValue(Time, "not measured"); //reset value
}
foreach (FieldInfo field in fi_inner)
{
s = string.Concat(s + field.GetValue(Time.stru2) + ";");
//field.SetValue(Time, "not measured"); //reset value
}
//and so one for each other struct
I want to achieve it like in the first example I gave.
Is this possible?
You can do this much easier without reflection. Change the struct and class like this:
public struct volt
{
private string[] _volts = new string[4];
public string[] volts {get {return _volts;} }
public string volt1 {
get {return _volts[0];}
set {_volts[0] = value;}
}
public string volt2 {
get {return _volts[1];}
set {_volts[1] = value;}
}
public string volt3 {
get {return _volts[2];}
set {_volts[2] = value;}
}
public string volt4 {
get {return _volts[3];}
set {_volts[3] = value;}
}
}
private class Injection
{
private _volt[] = new volt[5];
public volt[] {get {return _volt;} }
public volt stru1 {
get {return _volt[0];}
set {_volt[0] = value;}
}
public volt stru2 {
get {return _volt[1];}
set {_volt[1] = value;}
}
public volt stru3 {
get {return _volt[2];}
set {_volt[2] = value;}
}
public volt stru4 {
get {return _volt[3];}
set {_volt[3] = value;}
}
public volt stru5 {
get {return _volt[4];}
set {_volt[4] = value;}
}
public volt stru6 {
get {return _volt[5];}
set {_volt[5] = value;}
}
}
And now you have nice, convenient arrays you can use:
public void main()
{
Injection Time = new Injection();
//Here is code that fills Time with Time values as string type
string result = "";
foreach(volt v in Time.volt)
{
foreach(string s in v.volts)
{
result += s + ";"
}
}
Console.WriteLine(result);
}
FieldInfo properties that operate on 'field_inner' need a referece to an object of type 'volt_struct' so Time won't work here. You need to do a GetValue on 'field_outer' first, kind of like this:
foreach(FieldInfo field_outer in fi_outer)
{
var outer_object = field_outer.GetValue(Time);
if (outer_object == null) throw someexception;
foreach (FieldInfo field_inner in fi_inner)
{
s = string.concat(s+field_inner.GetValue(outer_object) + ";");
}
}
If you want to vary the parent and child types, you could pass them in as System.Type parameters, or you could write a Generic function with two type parameters. You might also move the 'fi_inner =' into the outer loop and do fi_inner = outer_object.GetType().GetFields(). That would concatenate strings on any child object whatever the type.
Related
I would like to deserialize an array containing some things and 3 List.
The program works fine except for List. The lists are created but they contain nothing !
Can you help me ?
Here is how the xml file looks like :
<blind>
<folder>C:\Users\Michael\Desktop\BT 1 normal\Programme BT\</folder>
<nombre_titres>25</nombre_titres>
<numero></numero>
<theme></theme>
<heure_debut></heure_debut>
<mdp>a</mdp>
<lien></lien>
<playlist>
<extrait>
<artiste>Abba</artiste>
<titre>Take a chance on me</titre>
<PointAT>1.25</PointAT>
<PointA>0.5</PointA>
<PointT>0.5</PointT>
<JoueursAT>
<joueurAT>Ahkayaqua</joueurAT>
<joueurAT>Angelene</joueurAT>
</JoueursAT>
<JoueursA>
<joueurA></joueurA>
</JoueursA>
<JoueursT>
<joueurT></joueurT>
</JoueursT>
</extrait>
<extrait>
....
</extrait>
</playlist>
</blind>
My code to deserialize :
XElement xmle;
xmle = XElement.Load(_folder + "Blind.xml");
textBox1.Text = xmle.Element("numero").Value;
textBox4.Text = xmle.Element("theme").Value;
textBox3.Text = xmle.Element("heure_debut").Value;
textBox5.Text = xmle.Element("lien").Value;
textBox2.Text = xmle.Element("mdp").Value;
extraits = (from ex in xmle.Element("playlist").Elements("extrait")
select new Extrait
(ex.Element("artiste").Value,
ex.Element("titre").Value,
0,
0,
0,
(from jat in ex.Element("JoueursAT").Elements("JoueurAT")
select jat.Element("JoueurAT").Value).ToList(),
(from ja in ex.Element("JoueursA").Elements("JoueurA")
select ja.Element("JoueurA").Value).ToList(),
(from jt in ex.Element("JoueursT").Elements("JoueurT")
select jt.Element("JoueurT").Value).ToList())).ToArray();
And here is my class:
public class Extrait
{
private String _Artiste;
private String _Titre;
private double _PointA;
private double _PointT;
private double _PointAT;
private List<String> _JoueurA;
private List<String> _JoueurT;
private List<String> _JoueurAT;
public String Artiste
{
get { return _Artiste; }
set { _Artiste = value; }
}
public String Titre
{
get { return _Titre; }
set { _Titre = value; }
}
public Double PointA
{
get { return _PointA; }
set { _PointA = value; }
}
public Double PointT
{
get { return _PointT; }
set { _PointT = value; }
}
public Double PointAT
{
get { return _PointAT; }
set { _PointAT = value; }
}
public List<String> JoueurA
{
get { return _JoueurA; }
set { _JoueurA = value; }
}
public List<String> JoueurT
{
get { return _JoueurT; }
set { _JoueurT = value; }
}
public List<String> JoueurAT
{
get { return _JoueurAT; }
set { _JoueurAT = value; }
}
public Extrait(String Artiste, String Titre, Double PointA, Double PointT, Double PointAT, List<String> JoueurAT, List<String> JoueurA, List<String> JoueurT)
{
_Artiste = Artiste;
_Titre = Titre;
_PointA = PointA;
_PointT = PointT;
_PointAT = PointAT;
_JoueurAT = JoueurAT;
_JoueurA = JoueurA;
_JoueurT = JoueurT;
}
}
Well, I've tried many possibilities, but none worked !
If this is your actual xml, then look at the inner tags - they start with lower letter. Your xml have <joueurAT> while you selecting elements with name .Elements("JoueurAT") - node names are case-sensitive.
Your code should look like:
extraits = (from ex in xmle.Element("playlist").Elements("extrait")
select new Extrait
(ex.Element("artiste").Value,
ex.Element("titre").Value,
0,
0,
0,
(from jat in ex.Element("JoueursAT").Elements("joueurAT")
select jat.Value).ToList(),
(from ja in ex.Element("JoueursA").Elements("joueurA")
select ja.Value).ToList(),
(from jt in ex.Element("JoueursT").Elements("joueurT")
select jt.Value).ToList())).ToArray();
This should fix it. Basically the default behaviour is to use two-level nesting for lists (meaning it defaults to [XmlArray] plus [XmlArrayItem]; you only have one-level here, so you need to tell it.
[XmlElement]
public List<String> JoueurA
{
get { return _JoueurA; }
set { _JoueurA = value; }
}
[XmlElement]
public List<String> JoueurT
{
get { return _JoueurT; }
set { _JoueurT = value; }
}
[XmlElement]
public List<String> JoueurAT
{
get { return _JoueurAT; }
set { _JoueurAT = value; }
}
Btw; you might find it more convenient to use something like auto-properties here; an example for both regular properties and lists:
public double PointAT {get;set;}
[XmlElement]
public List<string> JoueurA {get;} = new List<string>();
This is a lot more convenient than messing with all the fields yourself.
You probably also want to make sure you have a public parameterless constructor; frankly I'd just remove the custom constructor (in which case: a public parameterless constructor is included for free), but otherwise - I'd just add:
public Extrait() {}
The change is needed in the blind class
[XmlRoot("blind")]
public class Blind
{
[XmlArray("playlist")]
[XmlArrayItem("extrait")]
public List<Extrait> extrait { get; set; }
}
public class Extrait
{
}
(from jat in ex.Element("JoueursAT").Elements("JoueurAT")
select jat.Element("JoueurAT").Value).ToList()
should become
(from jat in ex.Element("JoueursAT").Elements("joueurAT")
select jat.Value).ToList()
I'm not sure if C#'s XML library is case sensitive when it comes to token names, but when in doubt, it's better to play it safe.
You also tried to access an element "joueurAT" when making your select despite having actually looped over them already, so you can just access the Value property directly.
Also instead of using LINQ the way you do, you could try the extension methods, which tend to be more readable :
xmle.Element("playlist").Elements("extrait")
.Select(ex => new Extrait
{
Artiste = ex.Element("artiste").Value,
Titre = ex.Element("titre").Value,
PointA = 0,
PointT = 0,
PointAT = 0,
JoueurA = ex.Element("JoueursAT").Elements("joueurAT").Select(jat => jat.Value).ToList(),
JoueurT = ex.Element("JoueursA").Elements("joueurA").Select(ja => ja.Value).ToList(),
JoueurAT = ex.Element("JoueursT").Elements("joueurT").Select(jt => jt.Value).ToList()
});
i'm kind of new to C# and i need some help regarding my assignment.
First off, i have to display a linkedlist containing an Object called Parcel and that Parcel
contains a few parameters int id, string name, int weight. When try to call a function to display whatever is inside the linkedlist, i get an error.
here is my DisplayInventory() function :
public void DisplayInventory()
{
for (Node j = head; j != null; j = j.Link )
{
Console.WriteLine(j.Data);
}
}
Here is my parcel class :
class Parcel
private int id;
private String customerName;
private int weight;
public Parcel(int id, String customerName, int weight)
{
this.id = id;
this.customerName = customerName;
this.weight = weight;
}
public int ID
{
get { return id; }
set { id = value; }
}
public String CustomerName
{
get { return customerName; }
set { customerName = value; }
}
public int Weight
{
get { return weight; }
set { weight = value; }
}
}
And here is my Node Class :
class Node
{
private object data;
public object Data
{
get { return data; }
set { data = value; }
}
private Node link;
internal Node Link
{
get { return link; }
set { link = value; }
}
public Node(object d)
{
this.data = d;
}
}
Everything runs fine except for my DisplayInventory() function found in my linkedlist.cs. It just displayed AppName.Parcel when i tried to print it out, i know i have to cast my j.data, but it doesn't work for me, any help? Thanks a lot.
You should print each property instead:
Console.WriteLine("Id: " + j.Data.Id.ToString());
Console.WriteLine("Name: " + j.Data.Name);
and so on.
You can call Console.WriteLine() for each field/property of your Parcel class or override it's ToString() method. It would look like that:
public class Parcel()
{
public override string ToString()
{
string str = ....// create here your string representation of Parcel
// if number of fileds is quite big use StringBuilder class
return str;
}
}
Console.WriteLine will call object.ToString() on your j.Data object which by default just returns the type name (Parcel).
I'm assuming DisplayInventory is inside the class which is implementing the linked list - in which case you should be able to refer to the properties of the class directly:
e.g.
Console.WriteLine(j.Id);
You could also override ToString on whatever j is (Parcel) by adding this to the source:
public override string ToString() { return this.Id.ToString(); }
Edit:
Ok in light of your update, you can just cast Node.Data (j.Data) to Parcel and access members directly:
for (Node j = head; j != null; j = j.Link )
{
// Cast using the as keyword - if the cast fails, parcel will be null, otherwise it will be the casted object
var parcel = j.Data as Parcel;
// Check if parcel is null, if not write some info
if(parcel != null)
{
Console.WriteLine(parcel.Id);
Console.WriteLine(parcel.CustomerName); // etc
}
}
Alternatively - just use j.Data.ToString() and ensure you have overridden the ToString member for Parcel
e.g. in Parcel.cs
// Override the ToString method. If you are using Visual Studio you should get a popup
// telling you which methods you can override after you type override then hit space
public override string ToString()
{
// string.format is one way of formatting the data, the tokens are replaced by the indexed arguments
return string.Format("{0} - {1}", Id, CustomerName);
}
I'm trying to compare two complex objects in C#, and produce a Dictionary containing the differences between the two.
If I have a class like so:
public class Product
{
public int Id {get; set;}
public bool IsWhatever {get; set;}
public string Something {get; set;}
public int SomeOtherId {get; set;}
}
And one instance, thus:
var p = new Product
{
Id = 1,
IsWhatever = false,
Something = "Pony",
SomeOtherId = 5
};
and another:
var newP = new Product
{
Id = 1,
IsWhatever = true
};
To get the differences between these, i'm doing stuff that includes this:
var oldProps = p.GetType().GetProperties();
var newProps = newP.GetType().GetProperties();
// snip
foreach(var newInfo in newProps)
{
var oldVal = oldInfo.GetValue(oldVersion, null);
var newVal = newInfo.GetValue(newVersion,null);
}
// snip - some ifs & thens & other stuff
and it's this line that's of interest
var newVal = newInfo.GetValue(newVersion,null);
Using the example objects above, this line would give me a default value of 0 for SomeOtherId (same story for bools & DateTimes & whathaveyou).
What i'm looking for is a way to have newProps include only the properties that are explicitly specified in the object, so in the above example, Id and IsWhatever. I've played about with BindingFlags to no avail.
Is this possible? Is there a cleaner/better way to do it, or a tool that's out there to save me the trouble?
Thanks.
There is no flag to tell if you a property was explicitly set. What you could do is declare your properties as nullable types and compare value to null.
If i understand you correctly, this is what microsoft did with the xml wrapping classes, generated with the xsd utility, where you had a XIsSpecified, or something like that, for each property X.
So this is what You can do as well - instead of public int ID{get;set;}, add a private member _id , or whatever you choose to call it, and a boolean property IDSpecified which will be set to true whenever Id's setter is called
I ended up fixing the issue without using reflection (or, not using it in this way at least).
It goes, more or less, like this:
public class Comparable
{
private IDictionary<string, object> _cache;
public Comparable()
{
_cache = new Dictionary<string, object>();
}
public IDictionary<string, object> Cache { get { return _cache; } }
protected void Add(string name, object val)
{
_cache.Add(name, val);
}
}
And the product implementation goes to this:
public class Product : Comparable
{
private int _id;
private bool _isWhatever;
private string _something;
private int _someOtherId;
public int Id {get { return _id; } set{ _id = value; Add("Id", value); } }
public bool IsWhatever { get { return _isWhatever; } set{ _isWhatever = value; Add("IsWhatever ", value); } }
public string Something {get { return _something; } set{ _something = value; Add("Something ", value); } }
public int SomeOtherId {get { return _someOtherId; } set{ _someOtherId = value; Add("SomeOtherId", value); } }
}
And the comparison is then pretty straightforward
var dic = new Dictionary<string, object>();
foreach(var obj in version1.Cache)
{
foreach(var newObj in version2.Cache)
{
//snip -- do stuff to check equality
dic.Add(....);
}
}
Doesn't hugely dirty the model, and works nicely.
I have a bunch of business class with autoproperties :
public class A {
public int Id { get; set; }
public string Title { get; set;}
}
Because the application evolves, there is a new requirement to enable tracking the changes of the properties, in order to send to the backing store only changed data.
In order to reach this goal, I have to convert ALL properties to field + property like this :
public class A {
private int m_Id;
public int Id {
get { return m_Id; }
set {
if(m_Id != value){
SetChanged("Id");
m_Id = value;
}
}
}
private string m_Title;
public string Title
{
get { return m_Title; }
set {
if(m_Title != value){
SetChanged("Title");
m_Title = value;
}
}
}
protecte void SetChanged(string propertyName) {
// Not important here
}
}
Is there a way to quickly refactor my code to avoid having to manually change the properties ?
There's no way in the IDE to do this, but if you need to replace all X properties, I would write a short console application to do it.
The process would be:
Iterate over all files in directory matching *.cs
Foreach file, regex find and replace old property for new property syntax
Using regex to match is very powerful. Regex can be used in VS2010 to do a find/replace operation. If you try finding this (with regex enabled)
{(public|private|internal|protected)}:b{[a-zA-Z0-9]+}
:b{[a-zA-Z0-9]+}:b\{ get; set; \}
It will match properties like this
public Type Foo { get; set; }
In your console application find all lines of code that match the above, then start splitting them up into Modifier, Type, Property Name and finally replacing the whole block with something like this
// PS: this is pseudocode ;-) or could be your new property template
private [Type] m_[PropertyName].ToPascaleCase
public [Type] PropertyName
{
get { return m_[PropertyName].ToPascaleCase; }
set
{
if(m_[PropertyName].ToPascaleCase != value){
SetChanged([PropertyName]);
m_[PropertyName].ToPascaleCase = value;
}
}
}
Finally I would advocate taking a backup of your code or running this test offline and testing before checking in!!
You can always just create a generic method that will do the assignment and call SetChange
void SetChangeIfNeeded<T>(ref T field, T value, string propertyName)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
SetChanged(property);
}
}
You would still need to have a private back field. Your class would look something like:
public class A {
private int m_id
public int Id
{
get { return m_id };
set { SetChangeIfNeeded<int>(ref m_id, value, "Id"); }
}
}
ReSharper can do this, but wouldn't modify setter.
public string Title {
get { return m_title; }
set { m_title = value; }
}
There is probably no direct way of doing this with refraction. If this was my problem. I would make code to generate this:
public string MakePropertyBigger(string varName, string propName, string dataType)
{
string output = "";
output += string.Format("private {0} {1};", dataType, varName) + Environment.NewLine;
output += string.Format("public {0} {1}", dataType, propName) + Environment.NewLine;
output += "{" + Environment.NewLine;
output += string.Format("get { return {0}; }", varName) + Environment.NewLine;
output += string.Format("set { if({0} != value){ SetChanged(\"{1}\");", varName, propName) + Environment.NewLine;
output += string.Format("{0} = value; }", varName) + Environment.NewLine;
output + "}" + Environment.NewLine + "}";
Now just plug this in and chug it out.
I've got something like this in my property/accessor method of a constructor for my program.
using System;
namespace BusinessTrips
{
public class Expense
{
private string paymentMethod;
public Expense()
{
}
public Expense(string pmtMthd)
{
paymentMethod = pmtMthd;
}
//This is where things get problematic
public string PaymentMethod
{
get
{
return paymentMethod;
}
set
{
if (string.IsNullOrWhiteSpace(" "))
paymentMethod = "~~unspecified~~";
else paymentMethod = value;
}
}
}
}
When a new attribute is entered, for PaymentMethod, which is null or a space, this clearly does not work. Any ideas?
do you perhaps just need to replace string.IsNullOrWhiteSpace(" ") with string.IsNullOrWhiteSpace(value) ?
From your posted code, you need to call:
this.PaymentMethod = pmtMthd;
instead of
paymentMethod = pmtMthd;
The capital p will use your property instead of the string directly. This is why it's a good idea to use this. when accessing class variables. In this case, it's the capital not the this. that makes the difference, but I'd get into the habit of using this.
Jean-Barnard Pellerin's answer is correct.
But here is the full code, which I tested in LinqPad to show that it works.
public class Foo {
private string _paymentMethod = "~~unspecified~~";
public string PaymentMethod
{
get
{
return _paymentMethod;
}
set
{
if (string.IsNullOrWhiteSpace(value))
_paymentMethod = "~~unspecified~~";
else _paymentMethod = value;
}
}
}
With a main of:
void Main()
{
var f = new Foo();
f.PaymentMethod = "";
Console.WriteLine(f.PaymentMethod);
f.PaymentMethod = " ";
Console.WriteLine(f.PaymentMethod);
f.PaymentMethod = "FooBar";
Console.WriteLine(f.PaymentMethod);
}
Output from console:
~~unspecified~~
~~unspecified~~
FooBar