mardi 27 octobre 2020

How do I identify List fields using reflection and add new objects to that list?

I have two objects that have mostly properties with the same type and name, and I have a method that assigns values from object A to the matching properties in object B. I'm writing a unit test which should call that method, and then assert that for the each property in object A that has a matching property in object B, the value was copied.

My idea to accomplish this is to use reflection to enumerate all the properties in object A and then assign a random value to each property. Then I call my "copy values" method to copy the values to object B, and then use reflection to enumerate the fields on each object and make sure that fields with matching names have the same value.

Here is my code to assign random values to object A

var objectA = new ObjectA();
var random = new Random();
var fields = typeof(ObjectA).GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var field in fields)
{
    if (field.CanWrite)
    {
        if (field.PropertyType == typeof(bool) || field.PropertyType == typeof(bool?))
        {
            field.SetValue(objectA, Convert.ToBoolean(random.Next(2)));
        }
        if (field.PropertyType == typeof(decimal) || field.PropertyType == typeof(decimal?))
        {
            field.SetValue(objectA, Convert.ToDecimal(random.Next()));
        }
        if (field.PropertyType == typeof(string))
        {
            field.SetValue(objectA, random.Next().ToString());
        }
        // similar code for other data types
    }
}

Then I call my method to copy the values:

ObjectB objectB = ObjectB.FromObjectA(objectA);

Then I call this method to compare the values of the two objects:

public static void AssertMatchingFieldAreEqual<T1, T2>(T1 a, T2 b)
{
    foreach (var fieldA in typeof(T1).GetProperties(BindingFlags.Instance | BindingFlags.Public))
    {
        var fieldInfoB = typeof(T2).GetProperty(fieldA.Name);
        if (fieldInfoB != null)
        {
            var propertyA = typeof(T1).GetProperty(fieldA.Name);
            var propertyB = typeof(T2).GetProperty(fieldA.Name);

            // Adding field names to make error messages for failed Assert calls list the field name
            var valueA = $"{propertyA.Name}: {propertyA.GetValue(a)}";
            var valueB = $"{propertyB.Name}: {propertyB.GetValue(b)}";

            Assert.AreEqual(valueA, valueB);
        }
    }
}

This works for basic data types. My problem is that I have some fields that are Lists, and I'd like to populate them with a random number of objects of their type, and then assert that when the fields are copied, each list has the same number of objects.

My two questions:

  1. When I'm assigning values, how do I check if a property is a List without knowing the type of item in the list? I've tried if (field.PropertyType == typeof(List<object>), but that doesn't work.

  2. How do I create a new object of type T add and it to my list when my property type is a list?

Or alternatively if there's a better way to check that my "copy values" method does copy all identically named fields, what's the better way?





Aucun commentaire:

Enregistrer un commentaire