C# recursively check all values not null - c#

Without wanting to reinvent the wheel, is there a .NET NuGet library to perform checks on a object recursively for argument checking?
If not, how would I convert the code to check if a property is null, and if a type that can hold properties of its own, recursively check that type, and end up with a list of property names that are null.
public static class Assert
{
public static void AllPropertiesNotNull<T>(T obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
var emptyProperties = typeof(T)
.GetProperties()
.Select(prop => new { Prop = prop, Val = prop.GetValue(obj, null) })
.Where(val => IsEmpty((dynamic)val.Val))
.Select(val => val.Prop.Name)
.ToList();
if (emptyProperties.Count > 0)
throw new ArgumentNullException(emptyProperties.First());
}
private static bool IsEmpty(object o) { return o == null; }
}

Note: You should do null checking on constructor parameters or method parameters and throw exception when the parameter is unexpectedly null. It's better to follow the common best practices.
Anyway, here is an example showing how you can check all properties of an object recursively using an extension method and throw exception of finding null properties...
You can create an extension method ThrowOnNullProperty for object and use it like this:
something.ThrowOnNullProperty();
Here is an implementation of such extension method:
If the passed object is null, throw exception.
If the object is a primitive type or a string, continue.
If the object has been visited before, then continue, otherwise add it to list of visited objects.
Check first level properties of the object and if there are null properties, throw an exception containing name of the null properties.
If the first level properties are not null, the for each property value go to 1.
Here is the code:
using System;
using System.Collections.Generic;
using System.Linq;
public static class ObjectExtensions
{
public static void ThrowOnNullProperty(this object obj)
{
ThrowOnNullProperty(obj, new HashSet<object>());
}
private static void ThrowOnNullProperty(object obj, HashSet<object> visitedObjects)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
if (obj.GetType().IsPrimitive || obj.GetType() == typeof(string))
return;
if (visitedObjects.Contains(obj))
return;
visitedObjects.Add(obj);
var nullPropertyNames = obj.GetType().GetProperties()
.Where(p => p.GetValue(obj) == null)
.Select(p => p.Name);
if (nullPropertyNames.Any())
throw new ArgumentException(
$"Null properties: {string.Join(",", nullPropertyNames)}");
var notNullPropertyValues = obj.GetType().GetProperties()
.Select(p => p.GetValue(obj))
.Where(v => v != null);
foreach (var item in notNullPropertyValues)
ThrowOnNullProperty(item, visitedObjects);
}
}

To do so, write a method to check the properties of current object and call it recursively on the non-null properties. I went ahead and wrote some code, which includes looping over dictionaries and enumerables and checking them for nulls also, taking into account circular references as mentioned by #dcg.
static readonly HashSet<Type> excludedTypes = new HashSet<Type>{ typeof(string) };
public static List<string> AllPropertiesNotNull(IDictionary dictionary, string name, HashSet<object> alreadyChecked)
{
List<string> nullValues = new List<string>();
foreach(object key in dictionary.Keys)
{
object obj = dictionary[key];
if (!alreadyChecked.Contains(obj))
{
string elementName = $"{name}[\"{key}\"]";
nullValues.AddRange(AllPropertiesNotNull(obj, elementName, alreadyChecked));
}
}
return nullValues;
}
public static List<string> AllPropertiesNotNull(IEnumerable enumerable, string name, HashSet<object> alreadyChecked)
{
List<string> nullValues = new List<string>();
int i = 0;
foreach (object obj in enumerable)
{
if (!alreadyChecked.Contains(obj))
{
string elementName = $"{name}[{i}]";
nullValues.AddRange(AllPropertiesNotNull(obj, elementName, alreadyChecked));
}
i++;
}
return nullValues;
}
public static List<string> AllPropertiesNotNull(object obj, string name, HashSet<object> alreadyChecked, string baseName = "")
{
List<string> nullValues = new List<string>();
string basePropertyName;
if (string.IsNullOrEmpty(baseName))
{
basePropertyName = name;
}
else
{
basePropertyName = baseName + "." + name;
}
if (obj == null)
{
nullValues.Add(basePropertyName);
}
else if (!alreadyChecked.Contains(obj))
{
alreadyChecked.Add(obj);
if (!excludedTypes.Contains(obj.GetType()))
{
foreach (PropertyInfo property in obj.GetType().GetProperties())
{
object value = property.GetValue(obj);
string propertyName = basePropertyName + "." + property.Name;
if (value == null)
{
nullValues.Add(propertyName);
}
else
{
if (typeof(IDictionary).IsAssignableFrom(property.PropertyType))
{
nullValues.AddRange(AllPropertiesNotNull((IDictionary)value, propertyName, alreadyChecked));
}
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
nullValues.AddRange(AllPropertiesNotNull((IEnumerable)value, propertyName, alreadyChecked));
}
else
{
nullValues.AddRange(AllPropertiesNotNull(value, property.Name, alreadyChecked, basePropertyName));
}
}
}
}
}
return nullValues;
}
I wrote some classes to test with:
class A
{
public string s1 { set; get; }
public string s2 { set; get; }
public int i1 { set; get; }
public int? i2 { set; get; }
public B b1 { set; get; }
public B b2 { set; get; }
}
class B
{
public string s1 { set; get; }
public string s2 { set; get; }
public int i1 { set; get; }
public int? i2 { set; get; }
public A a1 { set; get; }
public Dictionary<int, string> d1 { set; get; }
public List<A> l1 { set; get; }
}
and tested it as follows:
A a = new A
{
s1 = "someText"
};
B b = new B
{
s1 = "someText",
a1 = a,
d1 = new Dictionary<int, string>
{
{ 1, "someText" },
{ 2, null }
},
l1 = new List<A>{ null, new A { s1 = "someText" } , a }
};
a.b1 = b;
Console.WriteLine(string.Join("\n", AllPropertiesNotNull(a, nameof(a), new HashSet<object>())));
Output:
a.s2
a.i2
a.b1.s2
a.b1.i2
a.b1.d1["2"]
a.b1.l1[0]
a.b1.l1[1].s2
a.b1.l1[1].i2
a.b1.l1[1].b1
a.b1.l1[1].b2
a.b2
Few points to take note of:
Only public properties are considered, use BindingFlags if you want to consider non-public ones.
Some types might need to be individually considered (e.g.: string) or maybe not (depending on your own case).
As mentioned before, the code loops on dictionaries and enumerables and checks every value for them too. You might or might not want that (depending on your own case).

Related

How to get/set value of properties using string property paths?

Let's say we have a class Root that has BasicDetails class property and then basicDetails has two properties. A string "Name" and a dynamic object called "CustomAttributes".
I want to get the value in the following manner:
var root = new Root();
root.BasicDetails.Name = "Samurai";
root.BasicDetails.CustomAttributes.phone = "12345";
string res1 = GetDeepPropertyValue(root, "BasicDetails.CustomAttributes.Phone").ToString();
string res2 = GetDeepPropertyValue(root, "BasicDetails.Name").ToString();
The following is the code I have tried (based on another answer on SO):
public static object GetDeepPropertyValue(object src, string propName)
{
if (propName.Contains('.'))
{
string[] Split = propName.Split('.');
string RemainingProperty = propName.Substring(propName.IndexOf('.') + 1);
return GetDeepPropertyValue(src.GetType().GetProperty(Split[0]).GetValue(src, null), RemainingProperty);
}
else
return src.GetType().GetProperty(propName).GetValue(src, null);
}
The following are the classes:
public class Root
{
public Root()
{
BasicDetails = new BasicDetails();
}
public BasicDetails BasicDetails { get;set;}
}
public class BasicDetails
{
public BasicDetails()
{
CustomAttributes = new ExpandoObject();
}
public string Name { get; set; }
public dynamic CustomAttributes { get; set; }
}
The function that I have tried is throwing null reference error. Unfortunately I do not understand reflection too well so at the moment I am monkey patching. If someone could please explain c#how this can be done it would be great. Thank you in advance.
Following method gets values from nested properties:
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
if (propName.Contains("."))//complex type nested
{
var temp = propName.Split(new char[] { '.' }, 2);
return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
}
else
{
if (src is ExpandoObject)
{
var expando = src as IDictionary<string, object>;
if (expando != null)
{
object obj;
expando.TryGetValue(propName, out obj);
return obj;
}
return null;
}
else
{
var prop = src.GetType().GetProperty(propName);
return prop != null ? prop.GetValue(src, null) : null;
}
}
}
Usage:
string res1 = GetPropertyValue(root, "BasicDetails.CustomAttributes.phone") as string;
string res2 = GetPropertyValue(root, "BasicDetails.Name") as string;

Convert object by reflection

I want to convert an object A to object B. The classes A and B have the same properties, just the names are changed.
I use this method:
/// <summary>
internal static T objectMapper<T>(object objectSource, T objectTarget)
{
dynamic o = objectSource;
Type typeA = objectSource.GetType();
Type typeB = objectTarget.GetType();
IList<PropertyInfo> propsA = new List<PropertyInfo>(typeA.GetProperties());
IList<PropertyInfo> propsB = new List<PropertyInfo>(typeB.GetProperties());
dynamic s;
ArrayList listArray = new ArrayList();
foreach (var prop in propsA)
{
s = objectSource.GetType().GetProperty(prop.Name).GetValue(objectSource, null);
listArray.Add(s);
}
int i = 0;
foreach (var prop in propsB)
{
prop.SetValue(objectTarget, listArray[i], null);
i++;
}
return objectTarget;
}
How can I edit properties of objectB in the foreach loop? I want to use a generic method for different objects.
This solution provides both your reflection-way and an alternative way by defining and implementing a copy method CopyFrom. To reduce code you could make the interface a base-class so you don't need to implement CopyFrom in the sub-classes....
public interface MyInterface
{
int Prop1 { get; set; }
string Prop2 { get; set; }
void CopyFrom(MyInterface obj);
}
public class A: MyInterface
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public void CopyFrom(MyInterface obj)
{
this.Prop1 = obj.Prop1;
this.Prop2 = obj.Prop2;
}
}
public class B: MyInterface
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public void CopyFrom(MyInterface obj)
{
this.Prop1 = obj.Prop1;
this.Prop2 = obj.Prop2;
}
}
public static class CopyUtils
{
public static void Copy(MyInterface src, MyInterface dst)
{
var props = typeof(MyInterface).GetProperties();
foreach(var prop in props)
{
prop.SetValue(dst, prop.GetValue(src, null), null);
}
}
}
I feel there might be a deeper architecture issue here. I'm failing to imagine why would you want to "copy" the values of the properties from one object of a class to another of a different class with the same property names.
If you're trying to "shape" the object maybe just passing an interface will do the work
Anyhow, see if this helps:
public static class ObjectMorpher
{
public class InvalidMorphException : Exception
{
}
[AttributeUsage(AttributeTargets.Property)]
public class IgnoredOnMorphAttribute : Attribute
{
}
public static TargetType Morph<TargetType>(this object source, TargetType dest, Func<string, string> propertyMatcher = null, bool failOnNoMatch = false)
where TargetType : class
{
if (source == null || dest == null)
throw new ArgumentNullException();
foreach (var sourceProp in source.GetType().GetProperties().Where(x => x.GetCustomAttributes<IgnoredOnMorphAttribute>().Any() == false))
{
var destProp = typeof(TargetType).GetProperties().Where(x => x.Name == ((propertyMatcher == null) ? sourceProp.Name : propertyMatcher(sourceProp.Name))).FirstOrDefault();
//check property exists
if (destProp == null)
{
if (failOnNoMatch)
throw new InvalidMorphException();
else
continue;
}
//check value type is assignable
if (!destProp.GetType().IsAssignableFrom(sourceProp.GetType()))
{
if (failOnNoMatch)
throw new InvalidMorphException();
else
continue;
}
destProp.SetValue(dest, sourceProp.GetValue(source));
}
return dest;
}
}
Usage example:
var A = new ClassA();
var B = new ClassB();
B = A.Morph(B);
Optionally you can set a property match for the case when properties doesn't have the exact same name.
Also notice the use of the IgnoredOnMorph attribute to mark properties as not morph-able (like calculated properties)
You might find automapper of use here (see https://github.com/AutoMapper/AutoMapper/wiki/Getting-started).
You would need to create a line for each object mapping in a startup file to set it up but if the properties are the same this would be as simple as:
mapper.CreateMap<ClassA, ClassB>().ReverseMap();
And then a single line to resolve the mapping when needed
mapper.Map(objectOfClassA, new ClassB());

Generic solution to get default values of all properties in a range of objects

I want to get back the default values of all properties in a range of objects. The logic used is that if all of the property values in the range are the same, then use that for the default value, otherwise leave it null/type default.
I'm not sure if there is a better way to do this, but I'm open to all suggestions. I have created a working solution that is fairly generic, but I want it to be more so if possible. The current problem is that I have to have the if/elseif chain of the same code with a single difference of explicitly defining the type. I couldn't figure out how to get back the GetValue of the PropertyInfo and have the type properly pass into the generic functions. Once I got the object back, it would always pass down into the Generic as 'object' instead of 'int','decimal', etc. I also ran into the boxing/unboxing issue with nullables. I tried setting up the GetPropertyValue function with a generic return, but that requires you to pass in the type, which I'm not doing since I get it inside the function.
All of this code is just a working example. My classes have hundreds of properties and with 30 different classes, thats around 3000 properties I don't want to explicitly write out.
public class MainFunction
{
public MainFunction()
{
ParentClass defaultClass = new ParentClass();
List<ParentClass> results = MyDatabaseCallThatGetsBackListOfClass();
defaultClass = Generic.GetDefaultProperty(defaultClass, results);
}
private List<ParentClass> MyDatabaseCallThatGetsBackListOfClass()
{
List<ParentClass> populateResults = new List<ParentClass>();
for (int i = 0; i < 50; i++)
{
populateResults.Add(new ParentClass()
{
Class1 = new SubClass1()
{
Property1 = "Testing",
Property2 = DateTime.Now.Date,
Property3 = true,
Property4 = (decimal?)1.14,
Property5 = (i == 1 ? 5 : 25), // different, so should return null
Class1 = new SubSubClass1()
{
Property1 = "Test"
},
Class2 = new SubSubClass2()
},
Class2 = new SubClass2()
{
Property1 = null,
Property2 = 10,
Property3 = (i == 1 ? 15 : 30), // different, so should return null
Property4 = 20
}
});
}
return populateResults;
}
}
public class ParentClass
{
public ParentClass()
{
this.Class1 = new SubClass1();
this.Class2 = new SubClass2();
}
public SubClass1 Class1 { get; set; }
public SubClass2 Class2 { get; set; }
}
public class SubClass1
{
public SubClass1()
{
this.Class1 = new SubSubClass1();
this.Class2 = new SubSubClass2();
}
public string Property1 { get; set; }
public DateTime? Property2 { get; set; }
public bool? Property3 { get; set; }
public decimal? Property4 { get; set; }
public int? Property5 { get; set; }
public bool Property6 { get; set; }
public decimal Property7 { get; set; }
public DateTime Property8 { get; set; }
public int Property9 { get; set; }
public SubSubClass1 Class1 { get; set; }
public SubSubClass2 Class2 { get; set; }
}
public class SubClass2
{
public int? Property1 { get; set; }
public int? Property2 { get; set; }
public int Property3 { get; set; }
public int Property4 { get; set; }
}
public class SubSubClass1
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class SubSubClass2
{
public decimal? Property1 { get; set; }
public decimal Property2 { get; set; }
}
public static class Generic
{
public static T GetDefaultProperty<T>(T defaultItem, List<T> itemList)
where T : class
{
Type defaultType = defaultItem.GetType();
var props = defaultType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead);
foreach (var p in props)
{
if (p.PropertyType.IsClass && p.PropertyType != typeof(string))
{
dynamic classProperty = GetPropertyValue(defaultItem, p.Name);
var classList = GetClassSubList(itemList, classProperty, p.Name);
p.SetValue(defaultItem, GetDefaultProperty(classProperty, classList), null);
}
else
{
if (p.PropertyType == typeof(int?))
{
List<int?> subList = GetPropertySubList(itemList, TypeDefault<int?>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(bool?))
{
List<bool?> subList = GetPropertySubList(itemList, TypeDefault<bool?>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(decimal?))
{
List<decimal?> subList = GetPropertySubList(itemList, TypeDefault<decimal?>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(DateTime?))
{
List<DateTime?> subList = GetPropertySubList(itemList, TypeDefault<DateTime?>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(string))
{
List<string> subList = GetPropertySubList(itemList, TypeDefault<string>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(int))
{
List<int> subList = GetPropertySubList(itemList, TypeDefault<int>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(bool))
{
List<bool> subList = GetPropertySubList(itemList, TypeDefault<bool>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(decimal))
{
List<decimal> subList = GetPropertySubList(itemList, TypeDefault<decimal>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(DateTime))
{
List<DateTime> subList = GetPropertySubList(itemList, TypeDefault<DateTime>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
}
}
return defaultItem;
}
private static object GetPropertyValue<T>(T item, string propertyName)
{
if (item == null || string.IsNullOrEmpty(propertyName))
{
return null;
}
PropertyInfo pi = item.GetType().GetProperty(propertyName);
if (pi == null)
{
return null;
}
if (!pi.CanRead)
{
return null;
}
return pi.GetValue(item, null);
}
private static List<TReturn> GetClassSubList<T, TReturn>(List<T> list, TReturn returnType, string propertyName)
where T : class
where TReturn : class
{
return list.Select(GetClassSelection<T, TReturn>(propertyName)).ToList();
}
private static Func<T, TReturn> GetClassSelection<T, TReturn>(string fieldName)
where T : class
where TReturn : class
{
ParameterExpression p = Expression.Parameter(typeof(T), "t");
var body = Expression.Property(p, fieldName);
return Expression.Lambda<Func<T, TReturn>>(body, new ParameterExpression[] { p }).Compile();
}
private static List<TReturn> GetPropertySubList<T, TReturn>(List<T> list, TReturn returnType, string propertyName)
where T : class
{
return list.Select(GetPropertySelection<T, TReturn>(propertyName)).ToList();
}
private static Func<T, TReturn> GetPropertySelection<T, TReturn>(string fieldName)
where T : class
{
ParameterExpression p = Expression.Parameter(typeof(T), "t");
var body = Expression.Property(p, fieldName);
return Expression.Lambda<Func<T, TReturn>>(body, new ParameterExpression[] { p }).Compile();
}
private static T TypeDefault<T>()
{
return default(T);
}
You can switch the huge IF statement block with this:
var result = itemList.Select(x => p.GetValue(x, null)).Distinct();
if (result.Count() == 1)
{
p.SetValue(defaultItem, result.First(), null);
}
If you use Distinct(), references/value types are compared using object.Equals that first tests reference equality, and later actual values. This method has only one draw-back: boxing/unboxing. Use that code for reference types.
Note: there's already a lot of boxing happening in you code.
Reflection is based on a "object", so it's pretty hard not to have boxing issues.
For example:
Type defaultType = defaultItem.GetType(); // boxing on value types.
p.SetValue(defaultItem, subList.FirstOrDefault(), null); // boxing
Boxing is minor cost with reflection. You can run benchmarks to check.
As for your actual problem; you have list of objects and you want to compare them all, recursively. If there is no difference between two objects, you want to set the property in defaultItem to a property value that all objects share.
Ignoring the reason for whatever reason you're doing this(since I don't care; rather the solution to this problem is interesting), let's continue :P
Your best bet is to generate strongly-typed comparer on startup using reflection. Generate the code using StringBuilder and later use CSharpCodeProvider() to compile from StringBuilder and return strongly-typed delegate that has no reflection overhead. This is the fastest one I can think of, right now. The only hit it will take is the first interrogation of reflection metadata on startup. It's only per T.
On production code, you can cache that strongly typed comparer onto DLL, so the hit will be only one-time event.
private static class StrongClassComparer<T>
{
public static Func<T, string> GenerateMethod()
{
var code = new StringBuilder();
// generate your STRONGLY-TYPED code here.
// into code variable.
code.Append("public class YourClassInCode { "+
" public static string YourClassStaticMethod("+ typeof(T).Name+ " test)" +
" { return string.Empty; } }");
var compiler = new CSharpCodeProvider();
var parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add(typeof (T).Assembly.Location);
parameters.CompilerOptions = "/optimize + ";
var results = compiler.CompileAssemblyFromSource(parameters, code.ToString());
var #class = results.CompiledAssembly.GetType("YourClassInCode");
var method = #class.GetMethod("YourClassStaticMethod");
return (Func<T, string>) Delegate.CreateDelegate(typeof (Func<T, string>), method);
}
}

How to check for common objects between 2 generic lists

I have 2 lists that I need to check for common objects that are being passed to a generic wrapper.
The first list (selList) is a typed entity list. The ID field in this list is different, based on what the base type for the list being created.
The second list (masterList) is an anonymous IList that I know has 2 properties {ID, DESC} - ID (could be int or string), and description (string). I can get the value of the ID property in this list.
I would like to return an extension of the master list that has a boolean field indicating whether the item in the master list is contained in the selList.
I'm thinking that I'm somewhere along the lines of the Visitor pattern.
public class SelectionCriteria<T> : where T : class
{
public IList<T> EligibleList { get; private set; }
public IList LookupList { get; private set; }
}
LookupList = new List<object>
{
new { ID = "fid", DESC = "Record 1"},
new { ID = "Record2", DESC = "Record 2"},
new { ID = "Record3", DESC = "Record 3"},
new { ID = "Record4", DESC = "Record 4"},
};
EligibleList = new List<AssetClass>
{
new AssetClass { FEE_ID = "fid", ASSET_CLASS = "A" },
};
I should get the following results:
LookupList[0] == true
LookupList[1] == false
LookupList[2] == false
LookupList[3] == false
Is there a better way to solve this problem?
var results = LookupList.Select(l => EligibleList.Any(e => e.FEE_ID==l.ID))
.ToList();
Using this as a definition for SelectionCriteria<T>
public class SelectionCriteria<T>
where T : class
{
public IList<T> EligibleList { get; private set; }
public IList LookupList { get; private set; }
public SelectionCriteria(IList lookupList, IList<T> eligibleList)
{
LookupList = lookupList;
EligibleList = eligibleList;
}
public bool this[int index]
{
get
{
var element = LookupList[index];
foreach (var item in EligibleList)
{
if (item.Equals(element))
{
return true;
}
}
return false;
}
}
}
And this as a definition for AssetClass
public class AssetClass : IEquatable<AssetClass>
{
public string FEE_ID { get; set; }
public string ASSET_CLASS { get; set; }
public bool Equals(AssetClass other)
{
return !ReferenceEquals(other, null) && other.FEE_ID == FEE_ID && other.ASSET_CLASS == ASSET_CLASS;
}
//Check to see if obj is a value-equal instance of AssetClass, if it's not, proceed
// to doing some reflection checks to determine value-equality
public override bool Equals(object obj)
{
return Equals(obj as AssetClass) || PerformReflectionEqualityCheck(obj);
}
//Here's where we inspect whatever other thing we're comparing against
private bool PerformReflectionEqualityCheck(object o)
{
//If the other thing is null, there's nothing more to do, it's not equal
if (ReferenceEquals(o, null))
{
return false;
}
//Get the type of whatever we got passed
var oType = o.GetType();
//Find the ID property on it
var oID = oType.GetProperty("ID");
//Get the value of the property
var oIDValue = oID.GetValue(o, null);
//If the property type is string (so that it matches the type of FEE_ID on this class
// and the value of the strings are equal, then we're value-equal, otherwise, we're not
return oID.PropertyType == typeof (string) && FEE_ID == (string) oIDValue;
}
}
You can get elements that are found in the list of eligible items that exist in the list of lookup items like so:
for (var i = 0; i < assetClassSelectionCriteria.LookupList.Count; ++i)
{
Console.WriteLine("LookupList[{0}] == {1}", i, assetClassSelectionCriteria[i]);
}
You could also use the following for PerformReflectionEqualityCheck in AssetClass if you don't like seeing the reflection goodness
private bool PerformReflectionEqualityCheck(object o)
{
if (ReferenceEquals(o, null))
{
return false;
}
dynamic d = o;
try
{
return FEE_ID == (string) d.ID;
}
catch
{
return false;
}
}
If by "extension of the master list" you meant an extension method, then, instead of declaring an indexer on SelectionCriteria<T> to get the results, you could do something like this:
public static class SelectionCriteriaExtensions
{
public static bool IsLookupItemEligible<T>(this SelectionCriteria<T> set, int index)
where T : class
{
var element = set.LookupList[index];
foreach (var item in set.EligibleList)
{
if (item.Equals(element))
{
return true;
}
}
return false;
}
}
and call it like this:
assetClassSelectionCriteria.IsLookupItemEligible(0);

Comparing Object properties using reflection

I have two classes Address and Employee as follows:
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
public class Employee
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public Address EmployeeAddress { get; set; }
}
I have two employee instances as follows:
var emp1Address = new Address();
emp1Address.AddressLine1 = "Microsoft Corporation";
emp1Address.AddressLine2 = "One Microsoft Way";
emp1Address.City = "Redmond";
emp1Address.State = "WA";
emp1Address.Zip = "98052-6399";
var emp1 = new Employee();
emp1.FirstName = "Bill";
emp1.LastName = "Gates";
emp1.EmployeeAddress = emp1Address;
var emp2Address = new Address();
emp2Address.AddressLine1 = "Gates Foundation";
emp2Address.AddressLine2 = "One Microsoft Way";
emp2Address.City = "Redmond";
emp2Address.State = "WA";
emp2Address.Zip = "98052-6399";
var emp2 = new Employee();
emp2.FirstName = "Melinda";
emp2.LastName = "Gates";
emp2.EmployeeAddress = emp2Address;
Now how can I write a method which compares these two employees and returns the list of properties which have different values. So in this example I would like the result to be FirstName and Address.AddressLine1 .
Like LBushskin said, you do not have to do this. This is not the fastest way! Buy if you want, try this:
public static List<PropertyInfo> GetDifferences(Employee test1, Employee test2)
{
List<PropertyInfo> differences = new List<PropertyInfo>();
foreach (PropertyInfo property in test1.GetType().GetProperties())
{
object value1 = property.GetValue(test1, null);
object value2 = property.GetValue(test2, null);
if (!value1.Equals(value2))
{
differences.Add(property);
}
}
return differences;
}
You don't necessarily need reflection to perform the comparison. You can write a comparer class that takes two instances of Employee or Address, and compares each field that should match. For any that don't match, you can add a string (or PropertyInfo) element to some list to return to the caller.
Whether you return a PropertyInfo, MemberInfo, or just a string depends on what the caller needs to do with the result. If you actually need to visit the fields that contain differences, the PropertyInfo/MemberInfo may be better - but to just report the differences a string is probaby sufficient.
The main value of reflection would be to write a general purpose object comparer that could take two instances of any kind of object and compare their public fields and properties. This helps avoid writing repetetive comparison code over and over - but that doesn't seem like the case you're in.
Here is a generic and recursive solution based on Oskar Kjellin's awnser.
I have posted this code as gist as well, so you can check the latest version or star/clone/fork it :)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
protected List<KeyValuePair<Type, PropertyInfo>> RecrusiveReflectionCompare<T>(T first, T second)
where T : class
{
var differences = new List<KeyValuePair<Type, PropertyInfo>>();
var parentType = first.GetType();
void CompareObject(object obj1, object obj2, PropertyInfo info)
{
if (!obj1.Equals(obj2))
{
differences.Add(new KeyValuePair<Type, PropertyInfo>(parentType, info));
}
}
foreach (PropertyInfo property in parentType.GetProperties())
{
object value1 = property.GetValue(first, null);
object value2 = property.GetValue(second, null);
if (property.PropertyType == typeof(string))
{
if (string.IsNullOrEmpty(value1 as string) != string.IsNullOrEmpty(value2 as string))
{
CompareObject(value1, value2, property);
}
}
else if (property.PropertyType.IsPrimitive)
{
CompareObject(value1, value2, property);
}
else
{
if (value1 == null && value2 == null)
{
continue;
}
differences.Concat(RecrusiveReflectionCompare(value1, value2));
}
}
return differences;
}
public IEnumerable<PropertyInfo> GetNotEqualsProperties(Employee emp1, Employee emp2)
{
Type employeeType = typeof (Employee);
var properies = employeeType.GetProperties();
foreach (var property in properies)
if(!property.GetValue(emp1, null).Equals(property.GetValue(emp2, null))) //TODO: check for null
yield return property;
}
And for complex properties you have to override Equals method
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public override bool Equals(object obj)
{
if (obj as Address == null)
return false;
return ((Address) obj).AddressLine1.Equals(AddressLine1);
}
}
No need for reflection. Of course, this example is returning a string with the property names...if you need the actual PropertyInfo object, things would get a little more difficult, but not by much.
public static IEnumerable<string> DiffEmployees
(Employee one, Employee two)
{
if(one.FirstName != two.FirstName)
yield return "FirstName";
if(one.LastName != two.LastName)
yield return "LastName";
if(one.Address.AddressLine1 != two.Address.AddressLine1)
yield return "Address.AddressLine1";
// And so on.
}

Categories

Resources