Работаем с рефлектором в c#

В данной заметке представлены методы по работе с рефлектором в c#, в особенности получение значения по полному пути к свойству.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace App.Infrastructure
{
    public static class ReflectorUtil
    {
        /// <summary>
        /// Gets property type by path
        /// </summary>
        /// <param name="objType"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        public static Type GetPropType(Type objType, string path)
        {
            if (objType == null) throw new ArgumentNullException(nameof(objType));
            if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path));

            path = path.Replace(".[", "[").TrimStart('[');

            foreach (string propertyName in path.Split('.', '['))
            {
                int brackEnd = propertyName.IndexOf("]");
                if (brackEnd == -1)
                {
                    PropertyInfo property = objType.GetProperty(propertyName);
                    objType = property.PropertyType;
                }
                else
                {
                    Type iDictionaryType, iEnumerableType;
                    if (IsDictionary(objType, out iDictionaryType))
                    {
                        objType = iDictionaryType.GetGenericArguments()[1];
                    }
                    else if (IsEnumerable(objType, out iEnumerableType))
                    {
                        objType = iEnumerableType.GetGenericArguments()[0];
                    }
                    else throw new Exception("Incorrect objType");
                }
            }
            return objType;
        }

        /// <summary>
        /// Gets property value by path
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        public static object GetPropValue(object obj, string path)
        {
            if (obj == null) throw new ArgumentNullException(nameof(obj));
            if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path));
            if (IsPrimitive(obj.GetType())) throw new ArgumentNullException(nameof(obj));

            path = path.Replace(".[", "[").TrimStart('[');

            object currentObj = obj;
            foreach (string propertyName in path.Split('.', '['))
            {
                Type currentType = currentObj.GetType();
                int brackEnd = propertyName.IndexOf("]");
                if (brackEnd == -1)
                {
                    PropertyInfo property = currentType.GetProperty(propertyName);
                    currentObj = property.GetValue(currentObj, null);
                }
                else
                {
                    string index = propertyName.TrimEnd(']');
                    Type iDictionaryType, iEnumerableType;
                    if (IsDictionary(currentObj.GetType(), out iDictionaryType))
                    {
                        currentObj = typeof(ReflectorUtil).GetMethod(nameof(GetDictionaryElement))
                                                .MakeGenericMethod(iDictionaryType.GetGenericArguments())
                                                .Invoke(null, new object[] { currentObj, index });
                    }
                    else if (IsEnumerable(currentObj.GetType(), out iEnumerableType))
                    {
                        currentObj = typeof(ReflectorUtil).GetMethod(nameof(GetListElement))
                                                .MakeGenericMethod(iEnumerableType.GetGenericArguments())
                                                .Invoke(null, new object[] { currentObj, index });
                    }
                    else throw new Exception("Incorrect objType");
                }
                if (currentObj == null)
                    break;
            }
            return currentObj != null ? currentObj : GetDefaultValue(obj, path);
        }

        /// <summary>
        /// Gets default value
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        public static object GetDefaultValue(object obj, string path)
        {
            if (obj == null) throw new ArgumentException(nameof(obj));
            Type t = GetPropType(obj.GetType(), path);
            return GetDefaultValue(t);
        }

        /// <summary>
        /// Gets default value
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static object GetDefaultValue(Type t)
        {
            if (t.IsValueType && Nullable.GetUnderlyingType(t) == null)
                return Activator.CreateInstance(t);
            else
                return null;
        }

        /// <summary>
        /// Checks if type has IDictionary interface
        /// </summary>
        /// <param name="type"></param>
        /// <param name="interfaceType"></param>
        /// <returns></returns>
        public static bool IsDictionary(Type type, out Type interfaceType)
        {
            foreach (Type iType in type.GetInterfaces())
            {
                if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
                {
                    interfaceType = iType;
                    return true;
                }
            }
            interfaceType = null;
            return false;
        }

        /// <summary>
        /// Checks if type has IEnumerable interface
        /// </summary>
        /// <param name="type"></param>
        /// <param name="interfaceType"></param>
        /// <returns></returns>
        public static bool IsEnumerable(Type type, out Type interfaceType)
        {
            foreach (Type iType in type.GetInterfaces())
            {
                if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                {
                    interfaceType = iType;
                    return true;
                }
            }
            interfaceType = null;
            return false;
        }

        /// <summary>
        /// Checks if type is primitive
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool IsPrimitive(Type type)
        {
            if (type == typeof(string)
                    || type == typeof(long) || type == typeof(long?)
                    || type == typeof(int) || type == typeof(int?)
                    || type == typeof(bool) || type == typeof(bool?)
                    || type == typeof(double) || type == typeof(double?)
                    || type == typeof(float) || type == typeof(float?)
                    || type == typeof(DateTime) || type == typeof(DateTime?)
                    || type.IsEnum || IsNullableEnum(type))
                return true;
            return false;
        }

        /// <summary>
        /// Checks if is nullable enum
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static bool IsNullableEnum(Type t)
        {
            return t.IsGenericType &&
                   t.GetGenericTypeDefinition() == typeof(Nullable<>) &&
                   t.GetGenericArguments()[0].IsEnum;
        }

        /// <summary>
        /// Gets dictionary element
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dict"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static TValue GetDictionaryElement<TKey, TValue>(IDictionary<TKey, TValue> dict, object index)
        {
            TKey key = (TKey)Convert.ChangeType(index, typeof(TKey), null);
            return dict[key];
        }

        /// <summary>
        /// Gets list element
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static T GetListElement<T>(IEnumerable<T> list, object index)
        {
            return (T)list.ElementAt(Convert.ToInt32(index));
        }
    }
}