Reflection Cast Each Property to List<T> - c#

I am trying to use Reflection to loop through an objects properties and cast each property to a List<T>.
class MyObj
{
public List<A> As { get; set; }
public List<B> Bs { get; set; }
public List<C> Cs { get; set; }
public List<D> Ds { get; set; }
public List<E> Es { get; set; }
}
I have a method that takes an object and I am trying to cast the value of each property to a List<T> but it is not working.
void ProcessObject(object o)
{
foreach (PropertyInfo propertyInfo in this.GetType().GetProperties())
{
Type propertyType = propertyInfo.PropertyType;
Type propertyListType;
if (TryListOfWhat(propertyType, out propertyListType))
{
var propertyValue = (List<propertyListType>)propertyInfo.GetValue(this);
}
}
}
public static bool TryListOfWhat(Type type, out Type innerType)
{
Contract.Requires(type != null);
var interfaceTest = new Func<Type, Type>(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>) ? i.GetGenericArguments().Single() : null);
innerType = interfaceTest(type);
if (innerType != null)
{
return true;
}
foreach (var i in type.GetInterfaces())
{
innerType = interfaceTest(i);
if (innerType != null)
{
return true;
}
}
return false;
}
Now that I know the property is a List<T> and I have the type of T, how do I cast the property object value to List<T>?
I created another question to explain what I am actually trying to accomplish here: https://stackoverflow.com/questions/22975430/serialize-deserialize-inmemorydatabase

You could try using "dynamic" instead of "var" and keep the property as an object, then it should be the same as calling the List members as always without compile time checking.

Related

C# recursively check all values not null

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).

Get complex type properties from PropertyInfo

I trying to build a generic recursive function to iterate all properties / complex properties and return an array of all properties from the following structre:
public class Root
{
[FieldCodeItems(1, EnumFieldCode.INT, "ED", "0204")]
public int Prop1 { get; set; }
public Child Child { get; set; }
}
public class Child
{
[FieldCodeItems(1, EnumFieldCode.INT, "ED", "0208")]
public int PropChild1 { get; set; }
[FieldCodeItems(19, EnumFieldCode.ALPHANUMERIC, "ED", "0208")]
public string PropChild2 { get; set; }
public Child1 Child1 { get; set; }
}
public class Child1
{
[FieldCodeItems(1, EnumFieldCode.INT, "ED", "0211")]
public int PropChild3 { get; set; }
}
public class MyReturClass
{
public string FileCode { get; set; }
public string FieldCode { get; set; }
}
I can read all properties from Root class but I can't get the properties from the complex properties:
public static List<MyReturClass> GetItems<T>(T obj)
{
var ret = new List<MyReturClass>();
PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo property in properties)
{
IEnumerable<Attribute> attributes = property.GetCustomAttributes();
foreach (Attribute attribute in attributes)
{
//here I read values from a custom property
var tr = (FieldCodeItems)attribute;
var value = obj.GetType().GetProperty(property.Name).GetValue(obj, null);
if (value == null) continue;
ret.Add(new MyReturClass
{
FieldCode = tr.FieldCode,
FileCode = tr.FileCode
});
}
//If is complex object (Child, Child1 etc)
if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
{
//I would like pass the complex property as parameter, but at this moment
//the property variable is a PropertyInfo and I need the complex type
//to get properties values from this object
GetItems(property); //Error. Also tried with GetItems(property.PropertyType);
}
}
return ret;
}
I would like pass the complex property as parameter, but at this moment the property variable is a PropertyInfo and I need the complex type to get properties values from this object.
How can I get this object?
I searched here, here and here but the solutions don't solve my problem.
Edit - 03-08-2018
Finally I found the awswer here
I added this code to solve the problem:
//If is complex object (Child, Child1 etc)
if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
{
if (obj == null) { return null; }
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(property.Name);
if (info == null) { return null; }
var v = info.GetValue(obj, null);
if (v != null)
GetItems(v);
}
have a look at https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-examine-and-instantiate-generic-types-with-reflection
I am not sure if what you are looking for is going to work because in you EF you are configuring the classes as ComplexType. I am not sure the class itself knows its a complexType. You might end up creating a method on the DBcontext...

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());

How to iterate through nested properties of an object

I am trying to loop through all properties in an object including nested objects and objects in collections, to check if the property is of DateTime data type. If it is, convert the value to UTC time leaving everything intact including the structure and the value of other properties untouched.
The structure of my classes as follows:
public class Custom1 {
public int Id { get; set; }
public DateTime? ExpiryDate { get; set; }
public string Remark { get; set; }
public Custom2 obj2 { get; set; }
}
public class Custom2 {
public int Id { get; set; }
public string Name { get; set; }
public Custom3 obj3 { get; set; }
}
public class Custom3 {
public int Id { get; set; }
public DateTime DOB { get; set; }
public IEnumerable<Custom1> obj1Collection { get; set; }
}
public static void Main() {
Custom1 obj1 = GetCopyFromRepository<Custom1>();
// this only loops through the properties of Custom1 but doesn't go into properties in Custom2 and Custom3
var myType = typeof(Custom1);
foreach (var property in myType.GetProperties()) {
// ...
}
}
How do I loop through the properties in obj1 and traverse further down obj2 then obj3 then obj1Collection? The function have to be generic enough because the Type passed to the function cannot be determined at design/compile time. Conditional statements to test for Types should be avoided since they might be class Custom100
//avoid conditional statements like this
if (obj is Custom1) {
//do something
} else if (obj is Custom2) {
//do something else
} else if (obj is Custom3) {
//do something else
} else if ......
This is not a complete answer but I would start from here.
var myType = typeof(Custom1);
ReadPropertiesRecursive(myType);
private static void ReadPropertiesRecursive(Type type)
{
foreach (PropertyInfo property in type.GetProperties())
{
if (property.PropertyType == typeof(DateTime) || property.PropertyType == typeof(DateTime?))
{
Console.WriteLine("test");
}
if (property.PropertyType.IsClass)
{
ReadPropertiesRecursive(property.PropertyType);
}
}
}
I think it's far from the original question but ReadPropertiesRecursive above fall in infinite loop on
class Chain { public Chain Next { get; set; } }
I'd rather propose version with accumulation
private static void ReadPropertiesRecursive(Type type, IEnumerable<Type> visited)
{
foreach (PropertyInfo property in type.GetProperties())
{
if (property.PropertyType == typeof(DateTime) || property.PropertyType == typeof(DateTime?))
{
Console.WriteLine("test");
}
else if (property.PropertyType.IsClass && !visited.Contains(property.PropertyType))
{
ReadPropertiesRecursive(property.PropertyType, visited.Union(new Type[] { property.PropertyType }));
}
}
}
Check myType and if IsClass is true, then traverse into it. You'll probably want to make this recursive.
public static class ReadPropertiesExtension
{
public static void ReadPropertiesRecursive<T>(object value, Func<T, T> func)
{
if (value is null)
return;
if (value is IEnumerable)
{
IList collection = (IList)value;
foreach (var val in collection)
ReadPropertiesRecursive(val, func);
return;
}
var type = value.GetType();
foreach (PropertyInfo property in type.GetProperties())
{
if (property.PropertyType == type)
continue;
if (!property.CanRead)
continue;
var val = property.GetValue(value);
if (property.CanWrite && val is T param)
{
property.SetValue(value, func(param));
}
else if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
{
ReadPropertiesRecursive(val, func);
}
}
}
}
Here is a recursive function which also supports Enumerables.
It takes a parameter func and executes this method anticipating that func takes parameter T and returns same param.
You can call it like- ReadPropertiesExtension.ReadPropertiesRecursive<double>(value, func1);
Note: Currently, it doesn't support objects which contain dictionary in it.

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);
}
}

Categories

Resources