Replacement of switch statement - getting properties via a string name - c#

Basically, i have a system in place where my datagrid marks cells that have changed with a new background colour, to do this i have a method in the object that contains these properties that receives a string which is the name of the property to check, and then a switch statement that takes that string to check the correct property.
public Color HasChanged(string value)
{
switch (value)
{
case "CILRef":
if (_localShipment.cilRef != _originalShipment.cilRef)
{
return Colors.SkyBlue;
}
else
{
return Colors.White;
}
case "ArrivedAtPortDate":
if (_localShipment.arrivedAtPortDate != _originalShipment.arrivedAtPortDate)
{
return Colors.SkyBlue;
}
else
{
return Colors.White;
}
}
}
I've removed the rest of the properties for brevity.
Now i get the nagging sensation that there is a cleaner way to do this string>property without using a switch statement, but i can't for the life of me find anything on google, it's hard to search without some keyword to go on.
I'm also attempting to only save those properties that have changed, i was going to place any changed property name into an array, and then have a loop with yet another switch statement that checked that array and then saved the correct property. However this again seems untidy to me.
is there a cleaner solution to this, hopefully that could handle the addition of new properties without needing to add new code to the switch statements.
I can include the rest of the code that does this checking (namely the WPF binding on the datagrid, and a converter that calls the checking method with the property name as a string parameter) if needed.

You can write an extension method like:
public static object GetPropValue(this object o, string propName)
{
return o.GetType().GetProperty(propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase)
.GetValue(o);
}
and use it
if(localShipment.GetPropValue(value).Equals(originalShipment.GetPropValue(value)))
{
}

You could define a common interface to your properties, then create a dictionary of properties like so:
var properties = new Dictionary<string, IProperty>();
and access them like this:
properties["CILRef"]

I would say a switch statement is fine, however, you could do the body of the case in single lines using the condition operator
switch (value)
{
case "CILRef":
return _localShipment.cilRef != _originalShipment.cilRef ? Colors.SkyBlue : Colors.White;
case "ArrivedAtPortDate":
return _localShipment.arrivatedAtPortDate != _originalShipment.arrivedAtPortDate ? Colors.SkyBlue : Colors.White;
...
}
But you would still have repetitive code here, you could take this a step further and have a GetColor method which took a function in
public Colors GetColor(Func<bool> condition)
{
return condition() ? Colors.SkyBlue : Colors.White;
}
...
switch (value)
{
case "CILRef":
return GetColor(() => _localShipment.cilRef != _originalShipment.cilRef);
case "ArrivedAtPortDate":
return GetColor(() => _localShipment.arrivatedAtPortDate != _originalShipment.arrivedAtPortDate);
}
Looking at your code closer, it does appear you are comparing the same property on each shipping and you could literally reduce this to one check using Reflection
public Color HasChanged(string value)
{
var date1 = _localShipment.GetType()
.GetProperty(value)
.GetValue(_localShipment, null);
var date2 = _originalShipment.GetType()
.GetProperty(value)
.GetValue(_originalShipment, null);
return date1 != date2 ? Colors.SkyBlue : Colors.White;
}
For brevity, you could create an extension method to wrap up the Reflection part
public static T Get<T>(this object obj, string name)
{
return obj.GetType().GetProperty(name).GetValue(obj, null);
}
...
return _localShipment.Get<DateTime>(value) != _originalShipment.Get<DateTime>(value) ? Colors.SkyBlue : Colors.White;

Related

Checking if all values are null inside an initialised object

I have some code that reads data from Excel sheets & put to List.
Now here comes a scenario that user copy some valid data then leaves a lot of rows empty & again then copy valid data. This reads a lot of empty rows. I am using some old code for excel read & that might be used somewhere else so I don't wish to mess up with that.
The problem is I have a lot of object inside List that are initialised but all values are null. So I ended up checking all attribuets in if like:
if (lstConsolidatedData != null)
{
foreach (var lst in lstConsolidatedData)
{
if(lst.a!=null && lst.b!=null && lst.c!=null //& so on....)
{
//some stuff...
}
}
}
I know this is not wither best or maintainable way since excel sheets can be modified & thus whole columns list code need to be changed again & again for a single column added or removed from excel.
Is there any way to check all values inside lst for null in a single statement?
some thing like lst.all !=null
Any idea if not code will also work for me.
Please note lst==null is false
EDIT: using answers from below I am getting this error
EDIT:
o.GetType().GetProperties().Any(c => c.GetValue(o) == null) works for any null but What I actually want to delete rows which contains no data in all columns not for particular one or two column. Issue is not resolved by o.Item1 != null && ... also since name/number of columns changes frequently.
You can use reflection to do this, but beware that it carries performance losses over doing it explicitly.
public bool IsAnyPropertyNull(object o)
{
return o.GetType().GetProperties().Any(c => c.GetValue(o) == null);
}
There is, however, no built-in expression to reduce the o.Item1 != null && o.Item2 != null && ... type syntax.
Also, I'm assuming the values you're talking about are properties. You could also use GetFields or whatever you need if that's more appropriate.
As Mathew Huagen pointed out using reflection to get properties and then checking their values seems to be fine. However, this has a big caveat that is there may be some properties you don't want to be checked. To solve this,you can either put them inside a list (as Tim Schmelter mentioned), or you can decorate desired properties of ConsolidatedData with a custom attribute to distinguish them:
public class ConsolidatedData
{
public int Id { get; set; }
[NotNull]
public object PropertyOne { get; set; }
[NotNull]
public object PropertyTwo { get; set; }
}
public class NotNull : Attribute { }
Then checking against null is safe when you filter out the properties not having NotNull attribute:
ConsolidatedData v = new ConsolidatedData();
bool allNotNull = v.GetType().GetProperties()
.Where(p => null != (Attribute.GetCustomAttribute(p, typeof(NotNull)) as NotNull))
.All(p=>p.GetValue(v) !=null );
Here property Id is not get checked.
You could write an extension method for your ConsolidatedData like this;
public static bool IsAnyPropNull(this ConsolidatedData cdata)
{
return cdata.GetType().GetProperties().Any(p => p.GetValue(this) == null);
}
And you could then filter your list with a one-liner;
var filteredList = lstConsolidatedData.Where(cd => !cd.IsAnyPropNull()).ToList();
This will return a list of all object without nulls. If you want the nulls, remove the !, ofcourse.
Edit;
Wow, my answer is almost the same as Matthew Haugen's... both should work fine, even though the implementation is slightly different.
I ended up digging down into old code & before turning dataset into List I deleted empty rows using code:
//Deleting empty records from Datset
foreach (DataTable source in result.Tables)
{
for (int i = 0; i < source.Rows.Count; i++)
{
DataRow currentRow = source.Rows[i];
bool isEmpty = false;
foreach (var colValue in currentRow.ItemArray)
{
if (!string.IsNullOrEmpty(colValue.ToString()))
{
isEmpty = false;
break;
}
else
isEmpty = true;
}
if (isEmpty)
source.Rows[i].Delete();
}
}
result.AcceptChanges();
Don't forget .AcceptChanges() since it actually save the changes done to dataset without which no records are removed.

Check if a property has a specific attribute?

I'm using WCF RIA services to do a few small pieces of a web application; mainly populate/filter lists (I don't understand RIA well enough yet to trust I'm doing server side validation correct). One of the things I do is get a list of which fields have which generic type, by which I mean, strings are a text type, decimal, double, integer are numeric, etc. I do that with a LINQ query
Fields = type.GetProperties().Where(pi => pi.PropertyType == typeof(string) && pi.GetGetMethod() != null && pi.Name != "DisplayName")
.Select(pi => new FieldData
{
FieldName = CommonResources.AddSpacesToSentence(pi.Name, true),
FieldType = "Text"
}).....
The field DisplayName is a special field that should be ignored in lists, but as this application is growing I realize this isn't a very maintainable/expandable/buzzwordable way to go about this. What I really want is to know is if metadata for the DisplayName property has the attribute [Display(AutoGenerateField = false)]
Is there a way I can check for that in my LINQ?
Update:
After posting this I was able to slowly work out how to do this (I've never worked with Attributes in this way before). The answer given by King King looks nice and is very generic, but the way I wound up solving this was different, so if you're interested in another way, here's what I found. I added this to the LINQ query:
((DisplayAttribute)Attribute.GetCustomAttribute(pi, typeof(DisplayAttribute))).GetAutoGenerateField() == false
You can use the GetCustomAttributes method to filter properties with the given attribute:
...
.Where(pi => pi.GetCustomAttributes(typeof(DisplayAttribute), true).Any())
...
The true argument includes inheritance in attribute search.
You can try the following code the check against an attribute value of a property:
public bool CheckPropertyAttribute(Type type, string property,
Type attributeType, string attProp, object value)
{
var prop = type.GetProperty(property);
if (prop == null) return false;
return CheckPropertyAttribute(prop, attributeType, attProp, value);
}
public bool CheckPropertyAttribute(PropertyInfo prop, Type attributeType,
string attProp, object value){
var att = prop.GetCustomAttributes(attributeType, true);
if (att == null||!att.Any()) return false;
var attProperty = attributeType.GetProperty(attProp);
if (attProperty == null) return false;
return object.Equals(attProperty.GetValue(att[0], null),value);
}
Usage::
if(CheckPropertyAttribute(pi, typeof(DisplayAttribute), "AutoGenerateField", false)){
//...
}
NOTE: I provided 2 overloads, but in your case I think you just need to use the second overload (the case in which we already have some PropertyInfo).

Passing properties as parameters to be Got and Set

Well, I need to repeat same code for many properties.
I've seen examples taking Action delegates, but they don't fit quite well here.
I want something like this: (see explanation below)
Dictionary<Property, object> PropertyCorrectValues;
public bool CheckValue(Property P) { return P.Value == PropertyCorrectValues[P]; }
public void DoCorrection(Property P) { P.Value = PropertyCorrectValues[P]; }
.
I want to have a dictionary containing many properties and their respective "correct" values. (I know it's not well declared, but that's the idea). Properties are not necessarely inside my class, some of them are in objects of different assemblies.
A method bool CheckValue(Property). This method must access the actual value of the property and compare to the correct value.
And a method a void DoCorrection(Property). This one sets the property value to the correct value.
Remember I have many of those properties, I wouldn't like to call the methods by hand for each property. I'd rather iterate through the dicionary in a foreach statement.
So, the main question is in the title.
I've tried the by ref, but properties don't accept that.
Am I obligated to use reflection??? Or is there another option (if I need, reflection answer will be accepted as well).
Is there anyway I can make a dictionary with pointers in C#? Or some kind of assignment that changes the value of variable's target instead of changing the target to another value?
Thanks for the help.
You can do this using reflection. Get a list of the properties on the object of interest with typeof(Foo).GetProperties(). Your PropertyCorrectValues property can have type IDictionary<PropertyInfo, object>. Then use the GetValue and SetValue methods on PropertyInfo to perform the desired operations:
public bool CheckProperty(object myObjectToBeChecked, PropertyInfo p)
{
return p.GetValue(myObjectToBeChecked, null).Equals(PropertyCorrectValues[p]);
}
public void DoCorrection(object myObjectToBeCorrected, PropertyInfo p)
{
p.SetValue(myObjectToBeCorrected, PropertyCorrectValues[p]);
}
In addition to Ben's code I'd like to contribute the following code fragment:
Dictionary<string,object> PropertyCorrectValues = new Dictionary<string,object>();
PropertyCorrectValues["UserName"] = "Pete"; // propertyName
PropertyCorrectValues["SomeClass.AccountData"] = "XYZ"; // className.propertyName
public void CheckAndCorrectProperties(object obj) {
if (obj == null) { return; }
// find all properties for given object that need to be checked
var checkableProps = from props
in obj.GetType().GetProperties()
from corr in PropertyCorrectValues
where (corr.Key.Contains(".") == false && props.Name == corr.Key) // propertyName
|| (corr.Key.Contains(".") == true && corr.Key.StartsWith(props.DeclaringType.Name + ".") && corr.Key.EndsWith("." + props.Name)) // className.propertyName
select new { Property = props, Key = corr.Key };
foreach (var pInfo in checkableProps) {
object propValue = pInfo.Property.GetValue(obj, null);
object expectedValue = PropertyCorrectValues[pInfo.Key];
// checking for equal value
if (((propValue == null) && (expectedValue != null)) || (propValue.Equals(expectedValue) == false)) {
// setting value
pInfo.Property.SetValue(obj, expectedValue, null);
}
}
}
When using this "automatic" value correction you might also consider:
You cannot create a PropertyInfo object just by knowing the property name and independently of the declaring class; that's why I chose string for the key.
When using the same property name in different classes then you might need to change the code that is doing the actual assignment because the type between the correct value and the property type might differ.
Using the same property name in different classes will always perform the same check (see point above), so you might need a syntax for property names to restrict it to a specific class (simple dot notation, doesn't work for namespaces or inner classes, but might be extended to do so)
If needed you can replace the "check" and "assign" part with separate method calls, but it might be done inside the code block as stated in my example code.

Efficient way of initializing a generic class' fields and/or properties

I am writing a ConfigParser class, which reads from a config file structured like this:
[Section]
option1 = foo
option2 = 12
option3 = ;
...
The information read is actually stored in a Dictionary<string, string>. What i'd like to achieve is the following:
struct ConfigStruct
{
public string option1;
public int option2;
public char option3 { get; set; }
// Any other _public_ fields or properties
}
ConfigParser Cp = new ConfigParser("path/to/config/file"); // Loads content
ConfigStruct Cs = Cp.CreateInstance<ConfigStruct>("Section");
Console.WriteLine(Cs.option1); // foo
Console.WriteLine(Cs.option2.ToString()); // 12
Console.WriteLine(Cs.option3.ToString()); // ;
The struct (or class, it doesn't matter) ConfigStruct, is application-specific, and the ConfigParser class should know nothing about it. Basically, I want to parse the value from a specific option, and store it into the field/property with the same name. Parsing should be done according to the field/property type.
I've developed a stub method for it:
public T CreateInstance<T>(string Section) where T : new()
{
// Gets options dictionary from loaded data
Dictionary<string, string> Options = this.Data[Section];
T Result = new T();
Type StructType = Result.GetType();
foreach (var Field in StructType.GetFields())
{
if (!Options.ContainsKey(Field.Name))
continue;
Object Value;
if (Field.FieldType == typeof(bool))
Value = Boolean.Parse(Options[Field.Name]);
else if (Field.FieldType == typeof(int))
Value = Int32.Parse(Options[Field.Name]);
else if (Field.FieldType == typeof(double))
Value = Double.Parse(Options[Field.Name]);
else if (Field.FieldType == typeof(string))
Value = Options[Field.Name];
else if (Field.FieldType == typeof(char))
Value = Options[Field.Name][0];
// Add any ifs if needed
else { /* Handle unsupported types */ }
Field.SetValue(Result, Value);
}
foreach (var Property in StructType.GetProperties())
{
// Do the same thing with public properties
}
return Result;
}
Do you think this is the right approach to the problem? Or should I move the responsability of initializing the struct to the application logic instead of the ConfigParser class? I know it's more efficient, but using reflection I write this method only once, and works for every struct.
Should I use reflection to invoke Parse() so that I can avoid all those ifs? Or you'd rather make those conversions type by type, to prevent unexpected behaviour?
Thanks for your time.
Assuming there is a specific reason why you are not using app.config/web.config or other built-in configuration files.
I think this comes down to what the rest of the application is doing, but personally I would do it this way. It allows you to get the return type cleanly and you are not passing an extra stuct down the stack that you don't need to be.
Reflection is a fantastic tool but has some overhead so if the list of types is finite then specifying them manually is more efficient, or alternately only reflecting the unknown types. Also I would change your if blocks to a switch statement, you will gain efficiencies if the IL complier can fully optimise the condition block.
I think there is a simpler solution. You could use a custom section handler to store your settings, custom section handlers are well described here: http://devlicio.us/blogs/derik_whittaker/archive/2006/11/13/app-config-and-custom-configuration-sections.aspx).

Passing properties as parameters

I want to create a generalized helper method for LoadFromXML loading and validation. If the XML I'm loading from is incomplete, I do want it to fail completely without throwing an exception. Currently, my code looks like this (more or less)
public override bool Load(XElement source)
{
return new List<Func<XElement, bool>>
{
i => this.LoadHelper(i.Element(User.XML_Username), ref this._username, User.Failure_Username),
i => this.LoadHelper(i.Element(User.XML_Password), ref this._password, User.Failure_Password)
//there are many more invokations of LoadHelper to justify this architecture
}
.AsParallel()
.All(i => i.Invoke(source));
}
private bool LoadHelper(XElement k, ref string index, string failure)
{
if (k != null && k.Value != failure)
{
index = k.Value;
return true;
}
return false;
}
this._username is a private member variable that is used by the property this.Username. This is the current solution I have for this problem, but I'm facing one major issue: Since I cannot pass the property itself to the LoadHelper and Action<string> doesn't match the property :(, I'm circumventing the property setter logic right now.
For your own musings, before the LoadHelper abstraction, each of my List<Func<XElement, bool>>'s entries looked like this...
i => ((Func<XElement, bool>)(k => { if (k == null || k.Value == User.Failure_Username) return false;
{ this.Username = k.Value; return true; } })).Invoke(i.Element(User.XML_Username)),
Question: Does anyone know any way to do this without circumventing the property's setter logic?
Action doesn't match the property
If I read that right, you tried replacing the "ref string index", with "Action<string>" and then tried passing the Protperty. Close but not quite. How 'bout?
private bool LoadHelper(XElement k, Action<string> setter, string failure)
{
if (k != null && k.Value != failure)
{
setter(k.Value);
return true;
}
return false;
}
then
i => this.LoadHelper(i.Element(User.XML_Username), s=>{this.Username = s},
User.Failure_Username),
I've sometimes wondered how much it would bloat things for .Net to support an iProperty(of T) interface with two members, Get and Set, and automatically wrap fields and properties so that an iProperty(of T) parameter could be passed a field or property.
Using anonymous methods, one could create such a thing not too totally horribly by creating an xProperty class whose constructor took the methods necessary to get and set a property. One could define instances of the class for any properties that one wanted other classes to be able to manipulate directly. Things would be much nicer, though, if there were a standard interface. Unfortunately, I'm unaware of one existing.

Categories

Resources