| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- using System;
- using System.Collections.Generic;
- using System.Linq.Expressions;
- using System.Reflection;
- namespace StandardLibrary
- {
- public static class PropertyPathExtractor
- {
- /// <summary>
- /// 获得类型下的所有基本类型的访问路径
- /// </summary>
- /// <param name="type"></param>
- /// <returns></returns>
- public static List<string> GetLeafPropertyPaths(Type type)
- {
- if (type == null) return new List<string>();
- var visited = new HashSet<Type>();
- var paths = new List<string>();
- Traverse(type, "", paths, visited);
- return paths;
- }
- private static void Traverse(Type type, string prefix, List<string> paths, HashSet<Type> visited)
- {
- if (type == null || IsSimpleType(type)) return;
- if (!visited.Add(type)) // 防止循环引用
- return;
- var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
- foreach (var prop in properties)
- {
- if (prop.GetIndexParameters().Length > 0) continue; // 跳过索引器
- string currentPath = string.IsNullOrEmpty(prefix)
- ? prop.Name
- : $"{prefix}.{prop.Name}";
- if (IsSimpleType(prop.PropertyType))
- {
- // 只有叶子节点(基本类型)才加入路径
- paths.Add(currentPath);
- }
- else
- {
- // 非简单类型:继续递归,但不记录 currentPath 本身
- Traverse(prop.PropertyType, currentPath, paths, visited);
- }
- }
- visited.Remove(type); // 回溯(可选)
- }
- private static bool IsSimpleType(Type type)
- {
- if (type == null) return true;
- if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal))
- return true;
- if (type.IsEnum)
- return true;
- if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
- return IsSimpleType(type.GetGenericArguments()[0]);
- return false;
- }
- }
- public static class PropertyPathHelper
- {
- //PropertyPathHelper.GetPath(()=>a.b.c),输出a.b.c
- public static string GetPath<T>(Expression<Func<T>> expression)
- {
- return GetMemberPath(expression.Body);
- }
- private static string GetMemberPath(Expression expression)
- {
- switch (expression)
- {
- case MemberExpression memberExpr:
- if (memberExpr.Expression is ParameterExpression)
- return memberExpr.Member.Name; // 顶层变量(实际不会出现,因为是 Func<T>)
- string parentPath = GetMemberPath(memberExpr.Expression);
- return parentPath + "." + memberExpr.Member.Name;
- case ConstantExpression constExpr:
- // 静态成员或 null 常量,无法构成路径
- //throw new ArgumentException("表达式不能包含常量或静态成员");
- return constExpr.Value.ToString();
- default:
- throw new ArgumentException($"不支持的表达式类型: {expression.GetType().Name}");
- }
- }
- }
- public static class PropertyFinder
- {
- /// <summary>
- /// 查找指定对象的(非静态)属性中,值为 T 类型(或其派生类)的所有实例。
- /// 不递归,仅检查当前对象的属性。
- /// </summary>
- public static List<T> FindPropertiesOfType<T>(object obj) where T : class
- {
- if (obj == null)
- return new List<T>();
- var result = new List<T>();
- var type = obj.GetType();
- // 获取所有实例属性(包括私有属性,如不需要可调整 BindingFlags)
- var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
- foreach (var prop in properties)
- {
- if (!prop.CanRead)
- continue;
- // 跳过索引器(如 this[int i])
- if (prop.GetIndexParameters().Length > 0)
- continue;
- try
- {
- var value = prop.GetValue(obj) as T;
- if (value != null)
- {
- result.Add(value);
- }
- }
- catch (Exception)
- {
- // 忽略无法读取的属性(如抛异常的 lazy 属性等)
- }
- }
- return result;
- }
- }
- public static class ObjectPropertySetter
- {
- /// <summary>
- /// Sets the value of a nested property on the specified target object, navigating through the property path.
- /// </summary>
- /// <remarks>This method supports setting values on nested properties by navigating through the
- /// specified property path. If any intermediate property in the path is <see langword="null"/>, an instance of
- /// the corresponding type will be created, provided the type has a parameterless constructor. The final
- /// property in the path will be assigned the specified value, which will be converted to the property's type if
- /// necessary.</remarks>
- /// <param name="target">The object on which the property value will be set. Cannot be <see langword="null"/>.</param>
- /// <param name="propertyPath">The dot-separated path of the property to set. Each segment represents a property name in the hierarchy.</param>
- /// <param name="value">The value to assign to the specified property. The value will be converted to the property's type if
- /// necessary.</param>
- /// <exception cref="ArgumentException">Thrown if a property in the path does not exist on the corresponding object type.</exception>
- /// <exception cref="InvalidOperationException">Thrown if an intermediate property in the path is <see langword="null"/> and cannot be instantiated due to
- /// the absence of a parameterless constructor.</exception>
- public static void SetPropertyValue(object target, string propertyPath, object value)
- {
- var parts = propertyPath.Split('.');
- object current = target;
- for (int i = 0; i < parts.Length; i++)
- {
- var propName = parts[i];
- var prop = current.GetType().GetProperty(propName,
- BindingFlags.Public | BindingFlags.Instance);
- if (prop == null)
- throw new ArgumentException($"Property '{propName}' not found on type {current.GetType()}.");
- if (i == parts.Length - 1)
- {
- // 最后一级:赋值
- var convertedValue = ConvertValue(value, prop.PropertyType);
- prop.SetValue(current, convertedValue);
- }
- else
- {
- // 中间级:获取子对象,若为 null 则创建(仅支持无参构造)
- var subObject = prop.GetValue(current);
- if (subObject == null)
- {
- var subType = prop.PropertyType;
- if (subType.GetConstructor(Type.EmptyTypes) == null)
- throw new InvalidOperationException($"Cannot create instance of {subType} (no parameterless constructor).");
- subObject = Activator.CreateInstance(subType);
- prop.SetValue(current, subObject);
- }
- current = subObject;
- }
- }
- }
- /// <summary>
- /// Converts the specified value to the specified target type.
- /// </summary>
- /// <remarks>This method supports conversion between compatible types, including basic types and
- /// enums. If the target type is an enumeration, the method attempts to convert the value to the corresponding
- /// enum value.</remarks>
- /// <param name="value">The value to convert. Can be null.</param>
- /// <param name="targetType">The type to which the value should be converted. Must not be null.</param>
- /// <returns>The converted value as an object of the specified target type, or null if <paramref name="value"/> is null.</returns>
- /// <exception cref="InvalidOperationException">Thrown if the conversion cannot be performed, such as when the value is incompatible with the target type.</exception>
- private static object ConvertValue(object value, Type targetType)
- {
- if (value == null)
- {
- // 返回 targetType 的 default 值,而不是简单返回 null
- return GetDefaultValue(targetType);
- }
- var sourceType = value.GetType();
- if (targetType.IsAssignableFrom(sourceType))
- return value;
- // 尝试类型转换(支持基本类型)
- try
- {
- if (targetType.IsEnum)
- return Enum.ToObject(targetType, value);
- return Convert.ChangeType(value, targetType);
- }
- catch (Exception ex)
- {
- throw new InvalidOperationException($"Cannot convert {value} ({sourceType}) to {targetType}", ex);
- }
- }
- public static object GetDefaultValue(Type type)
- {
- if (type == null) throw new ArgumentNullException(nameof(type));
- // 引用类型(包括 string)或可空值类型(如 int?)的 default 是 null
- if (!type.IsValueType)
- return null;
- // 值类型:使用 Activator.CreateInstance 返回其零初始化值
- return Activator.CreateInstance(type);
- }
- }
- }
|