2

I am writing an extension method TrimSpaces on a object so that it recursively should be able to trim spaces. I was successful in trimming the spaces for the first level object, however, I am unable to do the same for the child objects.

As an example, consider the following class

public class Employee
{
    public string EmployeeID { get; set; }
    public string EmployeeName { get; set; }
    public DateTime HireDate { get; set; }
    public Department EmployeeDepartment { get; set; }
}

public class Department
{
    public int DepartmentID { get; set; }
    public string DepartmentName { get; set; }
}

In the above class I am currently able to trim spaces from Employee class properties but I am unable to trim the DepartmentName

Here is the code that I have written

    public static T TrimSpaces<T>(this T obj)
    {
        var properties = obj.GetType()
            .GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(prop => prop.PropertyType == typeof(string))
            .Where(prop => prop.CanWrite && prop.CanRead);
        foreach (var property in properties)
        {
            var value = (string)property.GetValue(obj, null);
            if (value.HasValue())
            {
                var newValue = (object)value.Trim();
                property.SetValue(obj, newValue, null);
            }
        }


        var customTypes =
            obj.GetType()
                .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                .Where(
                    prop =>
                        !prop.GetType().IsPrimitive && prop.GetType().IsClass &&
                        !prop.PropertyType.FullName.StartsWith("System"));

        foreach (var customType in customTypes)
        {
            ((object)customType.GetValue(obj).GetType()).TrimSpaces();
        }

        return obj;
    }
usaipavan
  • 103
  • 7

2 Answers2

2

When you loop through the properties, you are invoking this line:

((object)customType.GetValue(obj).GetType()).TrimSpaces(); 

Which invokes TrimSpaces passing the type of the object as obj. Instead you should pass the object it self like this:

((object)customType.GetValue(obj)).TrimSpaces();

In which case, the cast to object is not needed, so you can have this:

customType.GetValue(obj).TrimSpaces();
Yacoub Massad
  • 27,509
  • 2
  • 36
  • 62
0

As Yacoub pointed out, I have used GetValue and then called TrimSpaces. Just like the last time I have tried to do it that way, I got a null reference exception. I have written a null check at the starting of the method to avoid the same. Also, for reasons that I don't know yet, I was getting a "TargetParameterCountException". For that I have added a check to the customType I was searching for should not be a primitive type. With those changes the code seems to work.

As a side note, the object on which I was planning to do trimming is complex deep nesting object with various user defined classes as properties with at least 5 level deep nesting. I am still yet to figure out on why null reference exception is occurring and also the TargetParameterCountException

Below is the final code that I am using which is doing the job.I would update this answer if I have any bug fixes

Ok. I have figured out what was the issue and below is the code with comments updated. I am also leaving the old code so that anyone checking this question in future will know the context

 public static T TrimSpaces<T>(this T obj)
    {
        if (obj == null)
        {
            return obj;
        }

        //Iterates all properties and trims the values if they are strings
        var properties = obj.GetType()
            .GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(prop => prop.PropertyType == typeof(string))
            .Where(prop => prop.CanWrite && prop.CanRead);

        foreach (var property in properties)
        {
            var value = (string)property.GetValue(obj, null);
            if (value.HasValue())
            {
                var newValue = (object)value.Trim();
                property.SetValue(obj, newValue, null);
            }
        }


        // This is to take care of Lists. This iterates through each value
        // in the list.
        // For example, Countries which is a List<Country>
        var baseTypeInfo = obj.GetType().BaseType;
        if (baseTypeInfo != null && baseTypeInfo.FullName.Contains("List"))
        {
            int listCount = (int)obj.GetType().GetProperty("Count").GetValue(obj, null);
            for (int innerIndex = 0; innerIndex < listCount; innerIndex++)
            {
                object item = obj.GetType()
                    .GetMethod("get_Item", new Type[] { typeof(int) })
                    .Invoke(obj, new object[] { innerIndex });
                item.TrimSpaces();
            }
        }


        // Now once we are in a complex type (for example Country) it then needs to
        // be trimmed recursively using the initial peice of code of this method
        // Hence if it is a complex type we are recursively calling TrimSpaces
        var customTypes =
            obj.GetType()
                .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                .Where(
                    prop =>
                        !prop.GetType().IsPrimitive && prop.GetType().IsClass &&
                        !prop.PropertyType.FullName.StartsWith("System"));

        foreach (var customType in customTypes)
        {
            // If it's a collection, let the about piece of code take care
            // Only, normal types like, Code etc will be trimmed
            if (customType.GetIndexParameters().Length == 0)
            {
                customType.GetValue(obj).TrimSpaces();
            }
        }

        return obj;
    }

Old Code:

public static T TrimSpaces<T>(this T obj)
    {

        if (obj == null)
        {
            return obj;
        }

        var properties = obj.GetType()
            .GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(prop => prop.PropertyType == typeof(string))
            .Where(prop => prop.CanWrite && prop.CanRead);
        foreach (var property in properties)
        {
            var value = (string)property.GetValue(obj, null);
            if (value.HasValue())
            {
                var newValue = (object)value.Trim();
                property.SetValue(obj, newValue, null);
            }
        }


        var customTypes =
            obj.GetType()
                .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                .Where(
                    prop =>
                        !prop.GetType().IsPrimitive && prop.GetType().IsClass &&
                        !prop.PropertyType.FullName.StartsWith("System"));

        foreach (var customType in customTypes)
        {
            if (customType.Name.Contains("Item"))
            {
                continue;
            }

            customType.GetValue(obj).TrimSpaces();
        }

        return obj;
    }
usaipavan
  • 103
  • 7