Sunday, August 19, 2012

Enumerating a list object when you don’t know the actual type

Here is the scenario I was working with:

I needed to write an extension method that would be able to convert any class to a string representation. The class could have properties that were lists of any type (eg: List<T>), but I wouldn’t know in advance what types might be implemented as lists in the class. Here is the code I came up with. Listed below is the code for metro apps as well as for normal .net apps: (The code to enumerate any object that is a List is highlighted in the code below).

Metro

static class ObjectExtensions
    {
        public static string Convert(this object obj)
        {
            var typeInfo = obj.GetType().GetTypeInfo();
            var sb = new StringBuilder();

            foreach (var info in typeInfo.DeclaredProperties)
            {
                var val = info.GetValue(obj, null);
                string strVal;
                if (val != null)
                {
                    var valType = val.GetType();
                    var valTypeInfo = valType.GetTypeInfo();
                   
                    if ((val is string)
                        || valTypeInfo.IsValueType)
                    {
                        strVal = val.ToString();
                    }
                    else if (valType.IsArray ||
                        (valTypeInfo.IsGenericType
                            && (valTypeInfo.GetGenericTypeDefinition() == typeof(List<>))))
                    {
                        Type genericArgument = valType.GenericTypeArguments[0];

                        var genericEnumerator =
                            typeof(System.Collections.Generic.IEnumerable<>)
                                .MakeGenericType(genericArgument)
                                .GetTypeInfo()
                                .GetDeclaredMethod("GetEnumerator")
                                .Invoke(val, null);
                        IEnumerator enm = genericEnumerator as IEnumerator;
                        StringBuilder sbEnum = new StringBuilder();
                        sbEnum.AppendLine("List:");
                        while (enm.MoveNext())
                        {
                            var item = enm.Current;
                            sbEnum.AppendLine("Item: " + item.Convert());
                        }
                        strVal = sbEnum.ToString();
                    }
                    else{
                        strVal = val.Convert();
                    }
                }
                else
                {
                    strVal = "null";
                }
                sb.AppendLine(info.Name + ": " + strVal);
            }

            return sb.ToString();
        }
    }

Windows .Net

static class ObjectExtensions
    {
        public static string Convert(this object obj)
        {
            var props = obj.GetType().GetProperties();
            var sb = new StringBuilder();

            foreach (var info in props)
            {
                var val = info.GetValue(obj, null);
                string strVal;
                if (val != null)
                {
                    var valType = val.GetType();
                    if ((val is string)
                        || valType.IsValueType)
                    {
                        strVal = val.ToString();
                    }
                    else if (valType.IsArray ||
                        (valType.IsGenericType
                            && (valType.GetGenericTypeDefinition() == typeof(List<>))))
                    {
                        Type genericArgument = valType.GetGenericArguments()[0];

                        var genericEnumerator =
                            typeof(System.Collections.Generic.IEnumerable<>)
                                .MakeGenericType(genericArgument)
                                .GetMethod("GetEnumerator")
                                .Invoke(val, null);
                        IEnumerator enm = genericEnumerator as IEnumerator;
                        StringBuilder sbEnum = new StringBuilder();
                        sbEnum.AppendLine("List:");
                        while (enm.MoveNext())
                        {
                            var item = enm.Current;
                            sbEnum.AppendLine("Item: " + item.Convert());
                        }
                        strVal = sbEnum.ToString();
                    }
                    else
                    {
                        strVal = val.Convert();
                    }
                }
                else
                {
                    strVal = "null";
                }
                sb.AppendLine(info.Name + ": " + strVal);
            }

            return sb.ToString();
        }
    }

No comments: