Ok so at first I thought this was easy enough, and maybe it is and I'm just too tired - but here's what I'm trying to do. Say I have the following objects:
public class Container
{
public string Name { get; set; }
public List<Address> Addresses { get; set; }
}
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public List<Telephone> Telephones { get; set; }
}
public class Telephone
{
public string CellPhone { get; set; }
}
What I need to be able to do, is 'flatten' Containers property names in to a string (including ALL child properties AND child properties of child properties) that would look something like this:
Container.Name, Container.Addresses.AddressLine1, Container.Addresses.AddressLine2, Container.Addresses.Telephones.CellPhone
Does that make any sense? I can't seem to wrap it around my head.
I suggest you to mark all the classes, you need to grab, with custom attribute after that you could do something like this
class Program
{
static void Main(string[] args)
{
var lines = ExtractHelper.IterateProps(typeof(Container)).ToArray();
foreach (var line in lines)
Console.WriteLine(line);
Console.ReadLine();
}
}
static class ExtractHelper
{
public static IEnumerable<string> IterateProps(Type baseType)
{
return IteratePropsInner(baseType, baseType.Name);
}
private static IEnumerable<string> IteratePropsInner(Type baseType, string baseName)
{
var props = baseType.GetProperties();
foreach (var property in props)
{
var name = property.Name;
var type = ListArgumentOrSelf(property.PropertyType);
if (IsMarked(type))
foreach (var info in IteratePropsInner(type, name))
yield return string.Format("{0}.{1}", baseName, info);
else
yield return string.Format("{0}.{1}", baseName, property.Name);
}
}
static bool IsMarked(Type type)
{
return type.GetCustomAttributes(typeof(ExtractNameAttribute), true).Any();
}
public static Type ListArgumentOrSelf(Type type)
{
if (!type.IsGenericType)
return type;
if (type.GetGenericTypeDefinition() != typeof(List<>))
throw new Exception("Only List<T> are allowed");
return type.GetGenericArguments()[0];
}
}
[ExtractName]
public class Container
{
public string Name { get; set; }
public List<Address> Addresses { get; set; }
}
[ExtractName]
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public List<Telephone> Telephones { get; set; }
}
[ExtractName]
public class Telephone
{
public string CellPhone { get; set; }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = true, AllowMultiple = true)]
public sealed class ExtractNameAttribute : Attribute
{ }
Per my comment, you could use something like this if it will always be a generic List type that you want to link to a child type. IteratePropertiesRecursively is an iterator over the properties of the given type, that will recursively enumerate the properties of the type and all child types linked through a generic List.
protected void Test()
{
Type t = typeof(Container);
string propertyList = string.Join(",", IteratePropertiesRecursively("", t).ToArray<string>());
// do something with propertyList
}
protected IEnumerable<string> IteratePropertiesRecursively(string prefix, Type t)
{
if (!string.IsNullOrEmpty(prefix) && !prefix.EndsWith(".")) prefix += ".";
prefix += t.Name + ".";
// enumerate the properties of the type
foreach (PropertyInfo p in t.GetProperties())
{
Type pt = p.PropertyType;
// if property is a generic list
if (pt.Name == "List`1")
{
Type genericType = pt.GetGenericArguments()[0];
// then enumerate the generic subtype
foreach (string propertyName in IteratePropertiesRecursively(prefix, genericType))
{
yield return propertyName;
}
}
else
{
// otherwise enumerate the property prepended with the prefix
yield return prefix + p.Name;
}
}
}
Note: This code will not correctly handle a type that recursively includes itself as a type of one of its properties. Trying to iterate over such a type will result in a StackOverflowException, as pointed out by #Dementic (thanks!).
Related
public class Person {
string Name
string Address
int Age
.. 100+ more columns
}
var result = new List<Person>();
foreach (var item in result )
{
//loop column and trim the values.
}
I want the simplest way to loop the columns (assuming 100+ columns) where datatype is string then trim the value.
To rephrase in more C# terms: I want to update all properties and fields of an object that are of type string with trimmed value as item.StringProp = item.StringProp.Trim(). I don't want to manually write update for each property.
You could use reflection and Linq for filtering the properties of type string. From the OP, it looks like you are using Fields instead of properties. Please note it is unclear whether the Properties/Fields are public from OP, if you need to use public fields/properties, please use BindingFlags.Public
public List<T> TrimList<T>(List<T> source)
{
foreach(var property in typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(x=>x.FieldType== typeof(string)))
{
foreach(var personItem in source)
property.SetValue(personItem,Convert.ToString(property.GetValue(personItem)).Trim());
}
return source;
}
If properties, you could use
public List<T> TrimList<T>(List<T> source)
{
foreach(var property in typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance).Where(x=>x.PropertyType== typeof(string)))
{
foreach(var personItem in source)
property.SetValue(personItem,Convert.ToString(property.GetValue(personItem)).Trim());
}
return source;
}
Demo Code
Note: prior to .NET 4.5 you need to pass null as a second argument:
public List<T> TrimList<T>(List<T> source)
{
foreach(var property in typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance).Where(x=>x.PropertyType== typeof(string)))
{
foreach(var personItem in source)
property.SetValue(personItem,Convert.ToString(property.GetValue(personItem,null)).Trim());
}
return source;
}
As well as reflection, another way is to make it the responsibility of the Person class.
public class Person {
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
.. 100+ more columns
public void DoTrim()
{
this.Name = this.Name.Trim();
this.Address = this.Address.Trim();
... still need to code 100+ properties
}
}
The advantage is that you can call it like this
var result = new List<Person>();
...
for(int i=0; i < result.Count(); i++)
{
result[i].DoTrim();
}
Or you can control your data in the Person class when you set it and use local private variables.
public class Person {
private string name;
public string Name
{
get { return name; }
set { name = value.Trim(); }
}
private string address;
public string Address
{
get { return address; }
set { address= value.Trim(); }
}
....
This is how I would implement it:
class Program
{
static void Main(string[] args)
{
var obj = new Person
{
MyProperty = " A",
MyProperty1 = " A ",
MyProperty2 = "A ",
MyProperty3 = "A A A",
};
TrimStrings(obj);
}
public static void TrimStrings(object obj)
{
Type stringType = typeof(string);
var properties = obj.GetType().GetProperties().Where(x => x.PropertyType == stringType);
foreach(var property in properties)
{
string value = (string)property.GetValue(obj);
property.SetValue(obj, value?.Trim());
}
}
}
public class Person
{
public string MyProperty { get; set; }
public string MyProperty1 { get; set; }
public string MyProperty2 { get; set; }
public string MyProperty3 { get; set; }
}
Output:
{"MyProperty":"A","MyProperty1":"A","MyProperty2":"A","MyProperty3":"A
A A"}
You can use This Nuget Package
.After Install use it as bellow:
result.ForEach(x => x.AdjustString());
I have following two classes
public class Family
{
public string ChildName { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
}
I have an instance of Employee class as follows.
Employee employee = new Employee();
employee.Name = "Ram";
employee.Id = 77;
employee.Child = new Family() { ChildName = "Lava" };
I have a method which gets the property value based on the property name as follows:
public static object GetPropertyValue(object src, string propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return src.GetType().GetRuntimeProperty(propName).GetValue(src, null);
}
foreach (String part in nameParts)
{
if (src == null) { return null; }
Type type = src.GetType();
PropertyInfo info = type.GetRuntimeProperty(part);
if (info == null)
{ return null; }
src = info.GetValue(src, null);
}
return src;
}
In the above method,when I try to get property value of nested class like
GetPropertyValue(employee, "employee.Child.ChildName")
or
GetPropertyValue(GetPropertyValue(employee, "Family"), "ChildName"
doesn't return any value because type.GetRuntimeProperty(part) is always null.
Is there any way to fix this problem?
You problem lies in this line:
foreach (String part in nameParts)
Because you are iterating over each part of nameParts, you are also iterating over "employee", which of course is not a valid property.
Try either this:
foreach (String part in nameParts.Skip(1))
Or calling the method like this:
GetPropertyValue(employee, "Child.ChildName")
(Notice no "employee.", because you already pass in an employee)
The problem in this case is that when you split the string employee.Child.ChildName, the "employee" is the first part. However, employee is not a property of the source i.e. Employee Class.
Try this:
public class Program
{
public static void Main()
{
Employee employee = new Employee();
employee.Name = "Ram";
employee.Id = 77;
employee.Child = new Family() { ChildName = "Lava" };
GetPropertyValue(employee, "employee.Child.ChildName");
}
public class Family
{
public string ChildName { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
}
public static object GetPropertyValue(object src, string propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return src.GetType().GetRuntimeProperty(propName).GetValue(src, null);
}
nameParts = nameParts.Skip(1).ToArray();
foreach (String part in nameParts)
{
if (src == null) { return null; }
Type type = src.GetType();
PropertyInfo info = type.GetRuntimeProperty(part);
if (info == null)
{ return null; }
src = info.GetValue(src, null);
}
return src;
}
Here, i have skipped the first part of the string i.e. "employee". However, you can solve the problem by passing Child.ChildName
This question is around 2 years old, but I found a another working solution for you question, which is easy to understand. If you initialize the object in calling calss constructor you can use dot(.) notation to assign or read property. Example -
public class Family{
public string ChildName { get; set; }
}
public class Employee{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
public Employee(){
Child = new Family();
}
}
Employee emp = new Employee();
emp.Family.ChildName = "Nested calss attribute value";
I would like to recursively build an complex object.
public class Name
{
public string firstName {get;set;}
public string lastName {get;set;}
}
public class Address
{
public string city {get;set;}
public string state {get;set;}
public string street {get;set;}
public string zip {get;set;}
}
public class Customer
{
public Name customerName {get;set;}
public Address customerAddress {get;set;}
public Guid id {get;set;}
}
Lets say that Customer lives in an assembly that I'm loading on the fly :)
I want to instantiate a type of Customer and populate its properties.
Customer object has more custom objects and a Guid property. How can I use recursion to create the Customer object and its nested objects. I have some code below where I stumbled into the fact that I should be using recursion.
static object TraversePropertyInfo(object obj, Assembly assembly)
{
Console.WriteLine(obj.GetType().Name);
foreach(PropertyInfo pi in obj.GetType().GetProperties())
{
if(pi.PropertyType.IsClass && pi.PropertyType.Namespace != "System")
{
if(pi.PropertyType.UnderlyingSystemType.GenericTypeArguments.Count() > 0)
{
Console.WriteLine("\tIList<{0}>", pi.Name);
}
else
{
Console.WriteLine("\t{0}\t<class>", pi.Name);
object child = Activator.CreateInstance(assembly.GetType(pi.PropertyType.FullName)); // create the child instance
obj.GetType().GetProperty(pi.Name).SetValue(obj, child); // set the child on the parent
// but the child can have children...
// I should be using recurrsion here
}
}
else
{
Console.WriteLine("\t{0}\t{1}", pi.Name, pi.PropertyType);
}
}
return obj;
}
void Main()
{
Create<Customer>().Dump();
}
// Define other methods and classes here
public class Name
{
public string Firstname { get; set; }
public string Lastname { get; set; }
}
public class Address
{
public string City { get; set; }
public string State { get; set; }
public string Street { get; set; }
public string Zip { get; set; }
}
public class Customer
{
public Name CustomerName { get; set; }
public Address CustomerAddress { get; set; }
public Guid Id { get; set; }
}
public static T Create<T>()
{
var type = typeof(T);
return (T)Create(type);
}
public static object Create(Type type)
{
var obj = Activator.CreateInstance(type);
foreach(var property in type.GetProperties())
{
var propertyType = property.PropertyType;
if (propertyType.IsClass
&& string.IsNullOrEmpty(propertyType.Namespace)
|| (!propertyType.Namespace.Equals("System")
&& !propertyType.Namespace.StartsWith("System.")))
{
var child = Create(propertyType);
property.SetValue(obj, child);
}
}
return obj;
}
Maybe this will work:
static object TraversePropertyInfo(object obj, Assembly assembly)
{
Console.WriteLine(obj.GetType().Name);
// we stop the iteration when we reached the root-class "object"
// which won´t add any custom properties
if (obj.GetType() == typeof(object) return obj;
foreach(PropertyInfo pi in obj.GetType().GetProperties())
{
if(pi.PropertyType.IsClass && pi.PropertyType.Namespace != "System")
{
if(pi.PropertyType.UnderlyingSystemType.GenericTypeArguments.Count() > 0)
{
Console.WriteLine("\tIList<{0}>", pi.Name);
}
else
{
Console.WriteLine("\t{0}\t<class>", pi.Name);
object child = Activator.CreateInstance(assembly.GetType(pi.PropertyType.FullName)); // create the child instance
child = TraversePropertyInfo(child, child.GetType().Assembly);
obj.GetType().GetProperty(pi.Name).SetValue(obj, child); // set the child on the parent
// this will do the recursion
return obj;
}
}
else
{
Console.WriteLine("\t{0}\t{1}", pi.Name, pi.PropertyType);
}
}
return obj;
}
I am creating a custom attribute. And I will use it in multiple classes:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class Display : System.Attribute
{
public string Name { get; set; }
public string Internal { get; set; }
}
public class Class1
{
[Display(Name = "ID")]
public int ID { get; set; }
[Display(Name = "Name")]
public string Title { get; set; }
}
public class Class2
{
[Display(Name = "ID")]
public int ID { get; set; }
[Display(Name = "Name")]
public string Title { get; set; }
}
Here is working correctly but I want to make it as generic as possible, like the MVC example:
Class1 class1 = new Class1();
class1.Title.DisplayName () / / returns the value "Name"
The only thing I could do was generate a loop of my property, but I need to inform my typeof Class1
foreach (var prop in typeof(Class1).GetProperties())
{
var attrs = (Display[])prop.GetCustomAttributes(typeof(Display), false);
foreach (var attr in attrs)
{
Console.WriteLine("{0}: {1}", prop.Name, attr.Name);
}
}
Is there any way to do it the way I want?
You can't do what you've shown exactly because class1.Title is an expression that evaluates to a string. You are looking for metadata about the Title property. You can use Expression Trees to let you write something close. Here's a short helper class that pulls the attribute value out of a property in an expression tree:
public static class PropertyHelper
{
public static string GetDisplayName<T>(Expression<Func<T, object>> propertyExpression)
{
Expression expression;
if (propertyExpression.Body.NodeType == ExpressionType.Convert)
{
expression = ((UnaryExpression)propertyExpression.Body).Operand;
}
else
{
expression = propertyExpression.Body;
}
if (expression.NodeType != ExpressionType.MemberAccess)
{
throw new ArgumentException("Must be a property expression.", "propertyExpression");
}
var me = (MemberExpression)expression;
var member = me.Member;
var att = member.GetCustomAttributes(typeof(DisplayAttribute), false).OfType<DisplayAttribute>().FirstOrDefault();
if (att != null)
{
return att.Name;
}
else
{
// No attribute found, just use the actual name.
return member.Name;
}
}
public static string GetDisplayName<T>(this T target, Expression<Func<T, object>> propertyExpression)
{
return GetDisplayName<T>(propertyExpression);
}
}
And here's some sample usage. Note that you really don't even need an instance to get this metadata, but I have included an extension method that may be convenient.
public static void Main(string[] args)
{
Class1 class1 = new Class1();
Console.WriteLine(class1.GetDisplayName(c => c.Title));
Console.WriteLine(PropertyHelper.GetDisplayName<Class1>(c => c.Title));
}
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.
}