6

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 ......
leppie
  • 115,091
  • 17
  • 196
  • 297
Twisted Whisper
  • 1,166
  • 2
  • 15
  • 27

4 Answers4

7

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);
                }
            }
        }
Sunny
  • 4,765
  • 5
  • 37
  • 72
  • 2
    The key here is `property.PropertyType.IsClass`. `if (property.PropertyType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(property.PropertyType)) {}` is also added to cater for collections. Thanks – Twisted Whisper Oct 07 '14 at 07:20
  • 1
    How would I be able to change the value of that property using property.SetValue(model, Convert.ToDateTime(property.GetValue(model, null)).ToUniversalTime()); – TarakPrajapati May 08 '20 at 04:23
1

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 }));
        }
    }
}
elpolaco
  • 11
  • 1
0

Check myType and if IsClass is true, then traverse into it. You'll probably want to make this recursive.

dvallejo
  • 1,033
  • 11
  • 25
0
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.