how to recursively call a generic method analyzing

2019-07-24 00:11发布

问题:

I'm creating a method that will analyze an instance of a class that I have created, checking each of the properties on that class for string types and then checking if those string properties are null or empty.

Code:

public class RootClass
{
    public string RootString1 { get; set; }
    public string RootString2 { get; set; }
    public int RootInt1 { get; set; }

    public Level1ChildClass1 RootLevel1ChildClass11 { get; set; }
    public Level1ChildClass1 RootLevel1ChildClass12 { get; set; }
    public Level1ChildClass2 RootLevel1ChildClass21 { get; set; }
}

public class Level1ChildClass1
{
    public string Level1String1 { get; set; }
    public string Level1String2 { get; set; }
    public int Level1Int1 { get; set; }
}

public class Level1ChildClass2
{
    public string Level1String1 { get; set; }
    public string Level1String2 { get; set; }
    public int Level1Int1 { get; set; }

    public Level2ChildClass1 Level1Level2ChildClass11 { get; set; }
    public Level2ChildClass1 Level1Level2ChildClass12 { get; set; }
    public Level2ChildClass2 Level1Level2ChildClass22 { get; set; }
}

public class Level2ChildClass1
{
    public string Level2String1 { get; set; }
    public string Level2String2 { get; set; }
    public int Level2Int1 { get; set; }
}

public class Level2ChildClass2
{
    public string Level2String1 { get; set; }
    public string Level2String2 { get; set; }
    public int Level2Int1 { get; set; }
}

Not all the properties on the class are strings, some of them are instances of other classes, which have their own properties, which also need to be analyzed the same way. Basically, the method will return true if any of the properties are strings with a value on the RootClass or anywhere on sub-levels of the class (for example, if RootLevel1ChildClass11 has a string property with a value).

Here's what I have so far:

public static bool ObjectHasStringData<T>(this T obj)
{
    var properties = typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
    foreach (var property in properties)
    {
        Type propertyType = property.PropertyType;
        if (propertyType == typeof(string))
        {
            try
            {
                if (!String.IsNullOrEmpty(property.GetValue(obj, null) as string))
                    return true;
            }
            catch (NullReferenceException) { } // we want to ignore NullReferenceExceptions
        }
        else if (!propertyType.IsValueType)
        {
            try
            {
                if (ObjectHasStringData(property.GetValue(obj, null)))
                    return true;
            }
            catch (NullReferenceException) { } // we want to ignore NullReferenceExceptions
        }
    }
    return false;
}

this works great on the first layer (so any string within the RootClass), but once I start using it recursively on the if (ObjectHasStringData(property.GetValue(obj, null))) line, the return value of property.GetValue() is object, so when calling the method recursively, T is object.

I can get the Type of the current object, but how do I convert the object returned from property.GetValue() to the actual type of the property?

回答1:

I'd suggest not making that a generic method, just have it accept any object, and use GetType to get the type (unless it's null). The generics doesn't really seem to add anything of value here.

So, remove the type parameter, use obj.GetType(), and don't recurse if the object is null!

Also, (propertyType)obj) won't work, and if it would it would have no use. Casting is only for type safety and determining (at compile time) how to interact with an object. To System.Reflection it doesn't make any difference.

public static bool ObjectHasStringData( this object obj )
{
    if( obj == null )
        return false;
    var properties = obj.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
    foreach (var property in properties)
     ...
}