C# Setting Properties using Index - c#

I have a business class that contains many properties for various stock-exchange price types. This is a sample of the class:
public class Prices
{
public decimal Today {get; set;}
public decimal OneDay {get; set;}
public decimal SixDay {get; set;}
public decimal TenDay {get; set;}
public decimal TwelveDay {get; set;}
public decimal OneDayAdjusted {get; set;}
public decimal SixDayAdjusted {get; set;}
public decimal TenDayAdjusted {get; set;}
public decimal OneHundredDayAdjusted {get; set;}
}
I have a legacy system that supplies the prices using string ids to identify the price type.
E.g.
Today = "0D"
OneDay = "1D"
SixDay = "6D"
//..., etc.
Firstly, I load all the values to an IDictionary() collection so we have:
[KEY] VALUE
[0D] => 1.23456
[1D] => 1.23456
[6D] => 1.23456
...., etc.
Secondly, I set the properties of the Prices class using a method that takes the above collection as a parameter like so:
SetPricesValues(IDictionary<string, decimal> pricesDictionary)
{
// TODAY'S PRICE
string TODAY = "D0";
if (true == pricesDictionary.ContainsKey(TODAY))
{
this.Today = pricesDictionary[TODAY];
}
// OneDay PRICE
string ONE_DAY = "D1";
if (true == pricesDictionary.ContainsKey(ONE_DAY))
{
this.OneDay = pricesDictionary[ONE_DAY];
}
//..., ..., etc., for each other property
}
Is there a more elegant technique to set a large amount of properties?
Thanks,
j

Instead of using a string-to-decimal mapping and checking the dictionary repeatedly, use a delegate mapping/extension method:
public static class PriceConverter
{
private static readonly Dictionary<string, Action<Prices, decimal>> setters =
CreateSetterDictionary();
public static void SetPrice(this Prices p, string id, decimal newPrice)
{
Action<Prices, decimal> setter;
if (setters.TryGetValue(id, out setter))
setter(p, newPrice);
}
private static Dictionary<string, Action<Prices, decimal>>
CreateSetterDictionary()
{
var dic = new Dictionary<string, Action<Prices, decimal>>();
dic.Add("0D", (p, d) => p.Today = d);
dic.Add("1D", (p, d) => p.OneDay = d);
// etc.
return dic;
}
}
Then you can write prices.SetPrice("0D", 1.23456).
If you like, add a throw statement at the end of the SetPrice method to handle cases where the id doesn't match anything.

I would put the string variables into constants, rather than declare them every time you run the method:
private const string ONE_DAY = "D1";
If you expect the collection parameter to contain all or most of the possible values, then your code is probably cool. If you expect that the dictionary will have a small subset of the possible values, it might be more efficient to use a foreach loop and a switch statement to set values, rather then do a lookup for every possible value every time. It just depends on how many values you need to deal with and how many you get in each method call.

Define a dictionary of properties in the constructor e.g.
private Dictionary<int, PropertyInfo> propertyDictionary = new ...
MyClass()
{
this.propertyDictionary.Add(0, this.GetType().GetProperty("FirstProperty");
...
}
then access using an indexed property
decimal this[int index]
{
get
{
PropertyInfo property;
if (this.propertyDictionary.TryGetValue(index, out property))
{
// Not sure I remember the arguments right here:
property.SetValue(this, new object[] { value });
}
set
{
// Similar code
}
}
You could later on improve this code by automatically parsing the properties in the constructor using reflection,
adding all properties with an attribute that tells you what the id is.
(Instead of adding them manually in the constructor).

Just an idea:
interface IPrices_As_String{
string OD { get; set; }
// other properties here...
}
interface IPrices{
decimal Today{get; set;}
}
class Prices : IPrices, IPrices_As_String{
public decimal Today { get; set; }
public string IPrices_As_String.OD {
get { return this.Today.ToString(); }
set {
if(!String.IsNullOrEmpty(value)){
this.Today = decimal.Parse(value);
}
}
}
}
Then when I am setting the values from the legacy system, I will use the Prices class on the interface as IPrices_As_String like:
IPrices_As_String obj = new Prices();
// set values from the legacy system
IPrices obj2 = obj as IPrices; // will give me the correct object..
.
HTH.

The way I see it, you have a few options, depending on your skills, the way you are allowed to change the current POCO's or other classes:
If you must use a dictionary, create a similar dictionary which maps the "0D" etc to the OneDay names. Loop through the dictionary and assign using simple reflection.
If you can change the way the data is read, have the dictionary read with OneDay etc, instead of the "0D", which is only applicable to the external application.
Create an attribute, LegacyKeyAttribute, augment your POCO gettors/settors with this attribute. Now it becomes trivial: loop through the properties of the POCO to find the correct property for your current legacy key.
The last option requires a bit more understanding of C# than many average programmers know: writing and using attributes and reflection. However, in the end it's the cleanest and easiest solution (I'll try to come up with an example).
UPDATE: here's a little example. Meanwhile, many improvement suggestions have been posted, but none still uses attributes, while your case seems ideal. Why? It poses the least burden on existing code, I believe, and it makes reading and understanding your code even easier.
Usage:
// any price:
Prices prices = new Prices();
prices.SetPriceByLegacyName("0D", 1.2345M);
// or, your loop becomes a bit easier:
SetPricesValues(IDictionary<string, decimal> pricesDictionary)
{
foreach(string key in pricesDictionary.Keys)
{
// assuming "this" is of type Prices (you didn't specify)
this.SetPriceByLegacyName(key, pricesDictionary[key]);
}
}
The implementation:
// the simplest attribute class is enough for you:
[AttributeUsage(AttributeTargets.Property)]
public class LegacyNameAttribute : Attribute
{
public string Name { get; set; }
public LegacyNameAttribute(string name)
{
this.Name = name;
}
}
// your Prices POCO class becomes easier to read
public class Prices
{
[LegacyName("0D")] public decimal Today { get; set; }
[LegacyName("1D")] public decimal OneDay { get; set; }
[LegacyName("6D")] public decimal SixDay { get; set; }
[LegacyName("10D")] public decimal TenDay { get; set; }
[LegacyName("12D")] public decimal TwelveDay { get; set; }
[LegacyName("1DA")] public decimal OneDayAdjusted { get; set; }
[LegacyName("6DA")] public decimal SixDayAdjusted { get; set; }
[LegacyName("10DA")] public decimal TenDayAdjusted { get; set; }
[LegacyName("100DA")] public decimal OneHundredDayAdjusted { get; set; }
}
// an extension method to ease the implementation:
public static class PricesExtensions
{
public static void SetPriceByLegacyName(this Prices price, string name, decimal value)
{
if (price == null)
throw new ArgumentException("Price cannot be null");
foreach (PropertyInfo prop in price.GetType().GetProperties())
{
LegacyNameAttribute legNameAttribute = (LegacyNameAttribute)
Attribute.GetCustomAttribute(prop, typeof(LegacyNameAttribute));
// set the property if the attribute matches
if (legNameAttribute != null && legNameAttribute.Name == name)
{
prop.SetValue(price, value, null);
break; // nothing more to do
}
}
}
}
That's all there is to it. Even with all the added lines, it may well be that your total line count becomes less. But more importantly, it becomes easier to maintain and use.

Related

Deserializing JSON with multi-level nesting

Yet another mtgjson.com inspired question; none of the other, similar questions are getting me where I need to be. First, a couple lines of sample JSON (from mtgjson's AllPrices.json):
"00028782-6ec2-54fe-8633-2c906d8f1076": {"prices": {"mtgo": {}, "mtgoFoil": {}, "paper": {"2019-12-01": 0.15}, "paperFoil": {}}},
"00040b50-3b84-5cea-b663-70038b87fa08": {"prices": {"mtgo": {"2019-12-02": 0.02}, "mtgoFoil": {"2019-12-02": 0.02}, "paper": {"2019-12-01": 0.15}, "paperFoil": {"2019-12-01": 0.53}}}
Each parent object is a GUID and the Price Info; the Price Info is the four types of prices offered, and for each of those four types, the price data is Last Updated Date and Price.
The classes I've created (after lots of other approaches, all of which have failed):
public class price_Class
{
public string Updated { get; set; }
public decimal Price { get; set; }
}
public class PriceInfo
{
[JsonProperty("mtgo")] public price_Class mtgo { get; set; }
[JsonProperty("mtgoFoil")] public price_Class mtgof { get; set; }
[JsonProperty("paper")] public price_Class RegPrice { get; set; }
[JsonProperty("paperFoil")] public price_Class FoilPrice { get; set; }
}
And how I'm using it:
dynamic prices = JsonConvert.DeserializeObject(sJSON);
IDictionary<string, JToken> pricelist = prices;
foreach (var priceline in pricelist)
{
sUUID = priceline.Key.ToString();
PriceInfo pi = JsonConvert.DeserializeObject<PriceInfo>(priceline.Value.ToString());
Stepping through in debug mode, I see that prices seems fine; pricelist, also. The foreach defines priceline as I'd expect, and sUUID is correctly defined - but pi shows up with all four sets of price data as null - not just those that are null, but those that should have data.
priceline.value looks fine, to me:
{{
"mtgo": {},
"mtgoFoil": {},
"paper": {
"2019-12-01": 0.53
},
"paperFoil": {
"2019-12-01": 4.53
}
}}
When I expand pi in the Locals window, it shows the four classes (FoilPrice, RegPrice, mtgo, mtgof), but the contents are null.
What I need, in case it's not obvious, is to have pi.RegPrice and pi.FoilPrice defined, with a Date and Price, when that data actually exists in the JSON.
I'll admit, nested classes and JSON in general is still outside my comfort zone; I appreciate all help!
The price_Class is not adequate for deserialize your JSON object.
try with this:
public class PriceInfo
{
[JsonProperty("mtgo")] public Dictionary<string, decimal> mtgo { get; set; }
[JsonProperty("mtgoFoil")] public Dictionary<string, decimal> mtgof { get; set; }
[JsonProperty("paper")] public Dictionary<string, decimal> RegPrice { get; set; }
[JsonProperty("paperFoil")] public Dictionary<string, decimal> FoilPrice { get; set; }
}
using this tool you can find exactly what are your DTO
the problem are on your json data as i believe date are not send like this in json it come as array of integers and with specific order like day month year .
as well as you need to create your DTO similar to json even in types you can't map it till you set the same type of json in your DTO
public class Prices
{
public Mtgo mtgo { get; set; }
public MtgoFoil mtgoFoil { get; set; }
public Paper paper { get; set; }
public PaperFoil paperFoil { get; set; }
}
public class RootObject
{
public Prices prices { get; set; }
}
don't forget to tag all of them with [JsonProperty("json prop name ")]
Parsing and formatting utilities for JSON.
A central concept in lift-json library is Json AST which models the structure of a JSON document as a syntax tree.
sealed abstract class JValue
case object JNothing extends JValue // 'zero' for JValue
case object JNull extends JValue
case class JString(s: String) extends JValue
case class JDouble(num: Double) extends JValue
case class JInt(num: BigInt) extends JValue
case class JBool(value: Boolean) extends JValue
case class JField(name: String, value: JValue) extends JValue
case class JObject(obj: List[JField]) extends JValue
case class JArray(arr: List[JValue]) extends JValue
It comes with Lift, but non-Lift users can add lift-json as a dependency in following ways. Note, replace XXX with correct Lift version.
SBT users
Add dependency to your project description:
val lift_json = "net.liftweb" %% "lift-json" % "XXX"
Maven users
Add dependency to your pom:
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-json</artifactId>
<version>XXX</version>
</dependency>
Summary of the features:
Fast JSON parser
LINQ style queries
Case classes can be used to extract values from parsed JSON
Diff & merge
DSL to produce valid JSON
XPath like expressions and HOFs to manipulate JSON
Pretty and compact printing
XML conversions
Serialization
Low level pull parser API
Try using this for deeply nested JSONs.
It seems that the odd sub-structure of {Prices:{label:{date:amount}}} just doesn't work well with Newtonsoft's (otherwise excellent) JSON tools.
I tried the various tools (some suggested here) to generate classes; they were getting confused by the dates, creating classes for each date. I even tried generating classes for just the substring of data (priceline.value, in the example) - nope, still wouldn't work.
I ended going with a brute-force, string manipulation approach; it's ugly, I'm not exactly proud of it - but I now have what I needed. Here's the relevant snippets, just in case anyone else stumbles on the same things as I did:
private static string RemoveNoise(string input)
{
input = Regex.Replace(input, #"\r\n?|\n", string.Empty); // no more NewLine stuff
return input.Replace(" ", string.Empty)
.Replace(#"""",string.Empty);
}
...
public class PriceData
{
public string UUID { get; set; }
public string Updated { get; set; }
public string Price { get; set; }
public string FoilUpd { get; set; }
public string FoilPrc { get; set; }
}
...
string sPaperTag = #"PAPER:{";
string sPprFlTag = #"PAPERFOIL:{";
...
dynamic prices = JsonConvert.DeserializeObject(sJSON);
IDictionary pricelist = prices;
foreach (var priceline in pricelist)
{
PriceData pData = new PriceData();
pData.UUID = priceline.Key.ToString();
bool bWeHavePrice = false;
string pi = RemoveNoise(priceline.Value.ToString().ToUpper());
// parse out paper, paperFoil dates & prices manually (unusual JSON format...)
iBeg = pi.IndexOf(sPaperTag);
if (iBeg >= 0)
{
sTemp = pi.Substring(iBeg, pi.Length - iBeg);
iBeg = sTemp.IndexOf(":") + 2;
iEnd = sTemp.IndexOf("}");
sTemp = sTemp.Substring(iBeg, iEnd - iBeg); // either YYYY-MM-DD:n.nn, or an empty string
iBeg = sTemp.IndexOf(":");
if (iBeg > 0)
{
if (DateTime.TryParse(sTemp.Substring(0, iBeg), out dtTemp)) { pData.Updated = dtTemp.ToString(); bWeHavePrice = true; }
if (Decimal.TryParse(sTemp.Substring(++iBeg, sTemp.Length - iBeg), out decTemp)) { pData.Price = decTemp.ToString(); bWeHavePrice = true; }
}
}
I do that string manipulation dance again for the foil prices; I'm not currently interested in the 'mtgo' or 'mtgoFoil' data.
I'm doing all that TryParse stuff to make sure I have a valid date or amount, but I'm using the results to populate parameters in a SQLCommand, so I have to have strings; seems like extra work, going from string to Date or Decimal, then back to string - but this way I don't get exceptions when executing the SQL Insert command.
My thanks to all who helped, or tried to help. And if someone figures out how to handle it via JSON.Net, I'd love to see it!

How to access the object's specific properties from a list of objects sharing the same interface

I have an application where i have say 10 objects of different types. I wish to have them in same list and iterate through them on many occasions. I cant push them into one list because they are of different types. So i created an interface and created a property that all objects share. Now i have the list of objects and type of the list is the "interface". When i iterate through the object, i can't access the specific properties of the object because the compiler will only know at runtime what object it is. So if i try to code Object_A.Name, visual studio will show error because it doesn't know they type of object. I can obviously do an if else or something similar to find the type of object and cast it, but i want to know of there is a better way, or if this whole approach of having an interface is wrong and if i should have begun in a different direction.
In the code below, i want to get the Devname, which i can't because its not part of the interface, but belongs to every object. I could make it part of the interface, but every now and then i may need to get a specific property. hence wanting to know if there is a way to do it.
foreach (ICommonDeviceInterface device in Form1.deviceList)
{
if (device.DevName.Equals(partnername))
{
return device.Port[portNo].PortRef;
}
}
One way you could do this is by using reflection to try to get the property value of a named property from an object, using a helper method like:
public static object GetPropValue(object src, string propName)
{
return src?.GetType().GetProperty(propName)?.GetValue(src, null);
}
Credit for above code goes to: Get property value from string using reflection in C#
This requires no checking types or casting, it just returns the value of the property, or null if it doesn't contain the property.
In use it might look like:
private static void Main()
{
// Add three different types, which all implement the same interface, to our list
var devices = new List<ICommonDeviceInterface>
{
new DeviceA {DevName = "CompanyA", Id = 1},
new DeviceB {DevName = "CompanyB", Id = 2},
new DeviceC {Id = 3},
};
var partnerName = "CompanyB";
foreach (var device in devices)
{
// Try to get the "DevName" property for this object
var devName = GetPropValue(device, "DevName");
// See if the devName matches the partner name
if (partnerName.Equals(devName))
{
Console.WriteLine($"Found a match with Id: {device.Id}");
}
}
}
Classes used for the sample above:
interface ICommonDeviceInterface
{
int Id { get; set; }
}
class DeviceA : ICommonDeviceInterface
{
public int Id { get; set; }
public string DevName { get; set; }
}
class DeviceB : ICommonDeviceInterface
{
public int Id { get; set; }
public string DevName { get; set; }
}
class DeviceC : ICommonDeviceInterface
{
public int Id { get; set; }
}
Use "as" and "is" to know what type of interface
public class A : ICommonDeviceInterface
{
public int AMember;
}
public class B :ICommonDeviceInterface
{
public int BMember;
}
foreach (ICommonDeviceInterface device in Form1.deviceList)
{
if(device is A)
{
A a = device as A;
a.AMember = 100;
}
else if(device is B)
{
B b = device as B;
b.BMember = 123;
}
}

How to deserialize an immutable data structure?

How would I deserialize YAML to a immutable data structure?
e.g. I have this YAML:
Value: SomeString
Number: 99
And this data structure:
public class MyData
{
public MyData(string value, int number)
{
Value = value;
Number = number;
}
public string Value { get; }
public int Number { get; }
}
For this I'd to use the constructor. So somehow I'd need to first retrieve a Dictionary<string, object> parsed from the YAML respecting my class (so 99 would be int, not string), then scan my type for an appropriate constructor,
Although the question doesn't mention it, I'm assuming you are using YamlDotNet (or SharpYaml which is a fork of YamlDotNet)
YamlDotNet doesnt support deserializing into classes that do not have a default constructor - but one option to achieve what you want is to deserialize into an intermediate Builder type that is mutable which can produce the final type.
e.g.
public class MyDataBuilder
{
public string Value { get; set; }
public int Number { get; set; }
public MyData Build() => new MyData(Value, Number);
}
And then use something like:
deserializer.Deserialize<MyDataBuilder>(yaml).Build();
You would end up having to create a parallel set of builders for your whole model however, e.g. if MyData had a third parameter of type MyOtherData (I've changed the example to use records instead of classes to make it concise):
public record MyOtherData(string OtherValue);
public record MyData(string Value, int Number, MyOtherData otherData);
In which case we would need another Builder:
public class MyOtherDataBuilder
{
public string OtherValue { get; set; }
}
And MyDataBuilder would look like:
public class MyDataBuilder
{
public string Value { get; set; }
public int Number { get; set; }
public MyOtherDataBuilder MyOtherData { get; set; }
public MyData Build() => new MyData(Value, Number, MyOtherData.Build());
}
It's an old but surprisingly relevant question. Now, with records in C#, immutable collections in .net, lack of ability to deserialize immutable data is a blocker - there is no way we need to change all our data types just to be able to deserialize. One practical workaround that I found - is to convert yaml to json first, then deal with json your preferred way - System.Text.Json, Newtonsoft, etc.
Here is how to do is easiest way:
static string ConvertToJson(string yaml) {
object DeserializeYaml() =>
new DeserializerBuilder()
.Build()
.Deserialize(new StringReader(yaml))
?? throw new InvalidOperationException("Cannot deserialize yaml string:" + Environment.NewLine + yaml);
string SerializeYamlObjectToJson(object yamlObject) =>
new SerializerBuilder()
.JsonCompatible()
.Build()
.Serialize(yamlObject);
return SerializeYamlObjectToJson(DeserializeYaml());
}
The only disadvantage, potentially big, is performance. I feel, however, that it's rarely an important requirement for yaml.
use the FormatterServices.GetUninitializedObject API (this will NOT invoke any constructors at all) and then use reflection to set fields.
Code example:
var instance = FormatterServices.GetUninitializedObject(typeof(MyData));
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var type = typeof(MyData);
var stringField = type.GetField("_value", flags);
stringField.SetValue(instance, "SomeString");
var numberField = type.GetField("_number", flags);
numberField.SetValue(instance, 99);
MyData data = (MyData)instance;

How should I use properties and what should be structure of my class for using indexers across multiple classes

I need help as to how do I go about the structure of classes. How do I use Indexers? I want to have something like
Company.Employees[empId].Employee["Designation"].Salary
To be more specific something like
Grid.Rows[rowIndex].Columns["CurrentColumnName"].Width
Add a method like
public string this[string s]
{
get{
if(s == ...)
return this.property;
}
}
Yet, this seems to be more a Situation for Collections, but
see here for a complete example.
Actually indexers are used to get element by index, and your EmpId is not a good candidate for indexing as these may be compost or non sequential.
If you still want to use it here is the code. It will mimic as Indexer but its modified version.
class Employee
{
public int EmpId { get; set; }
public float Salary { get; set; }
public string Designation { get; set; }
}
class Employees
{
List<Employee> EmpList = new List<Employee>();
public Employee this[int empId]
{
get
{
return EmpList.Find(x => x.EmpId == empId);
}
}
}
I would rather have a method because I can make it generic.
public T GetPropertyValue<T>(string property)
{
var propertyInfo = GetType().GetProperty(property);
return (T)propertyInfo.GetValue(this, null);
}
var emp = employee.GetPropertyValue<Employee>("Designation");
var salary = emp.Salary;
That said... Be careful for having so many dot notations. When you get that NullReferenceException on your line in a log file, it is very difficult to find out what exactly was null. So rather break things up a bit and have more lines then you have less trouble of resolving bugs.

System.Reflection GetProperties method not returning values

Can some one explain to me why the GetProperties method would not return public values if the class is setup as follows.
public class DocumentA
{
public string AgencyNumber = string.Empty;
public bool Description;
public bool Establishment;
}
I am trying to setup a simple unit test method to play around with
The method is as follows and it has all the appropriate using statements and references.
All I'm doing is calling the following but it returns 0
PropertyInfo[] pi = target.GetProperties(BindingFlags.Public | BindingFlags.Instance);
But if I setup the class with private members and public properties it works fine.
The reason I didn't setup up the the class the old school way was because it has 61 properties and doing that would increase my lines of code to at least triple that. I would be a maintenance nightmare.
You haven't declared any properties - you've declared fields. Here's similar code with properties:
public class DocumentA
{
public string AgencyNumber { get; set; }
public bool Description { get; set; }
public bool Establishment { get; set; }
public DocumentA()
{
AgencyNumber = "";
}
}
I would strongly advise you to use properties as above (or possibly with more restricted setters) instead of just changing to use Type.GetFields. Public fields violate encapsulation. (Public mutable properties aren't great on the encapsulation front, but at least they give an API, the implementation of which can be changed later.)
Because the way you have declared your class now is using Fields. If you want to access the fields trough reflection you should use Type.GetFields() (see Types.GetFields Method1)
I don't now which version of C# you're using but the property syntax has changed in C# 2 to the following:
public class Foo
{
public string MyField;
public string MyProperty {get;set;}
}
Wouldn't this help in reducing the amount of code?
I see this thread is already four years old, but none the less I was unsatisfied with the answers provided. OP should note that OP is referring to Fields not Properties. To dynamically reset all fields (expansion proof) try:
/**
* method to iterate through Vehicle class fields (dynamic..)
* resets each field to null
**/
public void reset(){
try{
Type myType = this.GetType(); //get the type handle of a specified class
FieldInfo[] myfield = myType.GetFields(); //get the fields of the specified class
for (int pointer = 0; pointer < myfield.Length ; pointer++){
myfield[pointer].SetValue(this, null); //takes field from this instance and fills it with null
}
}
catch(Exception e){
Debug.Log (e.Message); //prints error message to terminal
}
}
Note that GetFields() only has access to public fields for obvious reasons.
As mentioned, these are fields not properties. The property syntax would be:
public class DocumentA {
public string AgencyNumber { get; set; }
public bool Description { get; set; }
public bool Establishment { get; set;}
}

Categories