274 lines
11 KiB
C#
274 lines
11 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Diagnostics;
|
||
using System.Diagnostics.CodeAnalysis;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
using System.Text.Json;
|
||
|
||
namespace Falcon.SugarApi
|
||
{
|
||
/// <summary>
|
||
/// Object类型扩展方法
|
||
/// </summary>
|
||
public static class ObjectExtend
|
||
{
|
||
/// <summary>
|
||
/// 对source进行浅表复制,并复制到target中
|
||
/// </summary>
|
||
/// <typeparam name="TSource">原对象类型</typeparam>
|
||
/// <param name="source">原对象</param>
|
||
/// <param name="target">目标对象</param>
|
||
/// <param name="ignoreCase">忽略大小写。默认false不忽略大小写</param>
|
||
public static TSource CloneTo<TSource>(this TSource source,[NotNull] object target,bool ignoreCase = false) where TSource : class {
|
||
_ = source ?? throw new ArgumentNullException(nameof(source));
|
||
_ = target ?? throw new ArgumentNullException(nameof(target));
|
||
var all = from s in source.GetType().GetProperties()
|
||
join t in target.GetType().GetProperties()
|
||
on ignoreCase ? s.Name.ToLower() : s.Name equals ignoreCase ? t.Name.ToLower() : t.Name
|
||
select new { s,t };
|
||
foreach(var item in all) {
|
||
item.t.SetValue(target,item.s.GetValue(source).ChangeType(item.t.PropertyType));
|
||
}
|
||
return source;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从原对象中浅表复制属性值
|
||
/// </summary>
|
||
/// <typeparam name="Ttarget">目标对象类型</typeparam>
|
||
/// <param name="target">目标对象</param>
|
||
/// <param name="source">原对象</param>
|
||
/// <param name="ignoreCase">忽略大小写。默认false不忽略大小写</param>
|
||
/// <returns>目标对象</returns>
|
||
public static Ttarget CloneFrom<Ttarget>(this Ttarget target,object source,bool ignoreCase = false) where Ttarget : class {
|
||
source.CloneTo(target,ignoreCase);
|
||
return target;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将对象转换成另一类型,如果转换失败可能返回null。
|
||
/// </summary>
|
||
/// <param name="source">原对象</param>
|
||
/// <param name="targetType">目标类型</param>
|
||
/// <returns>转换后的类型</returns>
|
||
public static object? ChangeType(this object? source,Type targetType) {
|
||
if(targetType == null) {
|
||
throw new ArgumentNullException("targetType");
|
||
}
|
||
if(source == null) {
|
||
return null;
|
||
}
|
||
if(targetType.IsGenericType && targetType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) {
|
||
NullableConverter nullableConverter = new NullableConverter(targetType);
|
||
targetType = nullableConverter.UnderlyingType;
|
||
}
|
||
return Convert.ChangeType(source,targetType);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 如果对象为null则抛出异常
|
||
/// </summary>
|
||
/// <param name="obj">检测对象</param>
|
||
/// <returns>对象本身</returns>
|
||
/// <exception cref="ArgumentNullException">对象为null</exception>
|
||
public static object? ThrowNullExceptionWhenNull(this object? obj) {
|
||
if(obj == null) {
|
||
throw new ArgumentNullException();
|
||
}
|
||
return obj;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 扩展对象,获取属性枚举s
|
||
/// </summary>
|
||
/// <param name="obj">要扩展的对象</param>
|
||
/// <returns></returns>
|
||
public static IEnumerable<ExpandPropertyInfo> ExpandProperties(this object obj) {
|
||
foreach(PropertyInfo p in obj.GetType().GetProperties()) {
|
||
yield return new ExpandPropertyInfo {
|
||
TargetObj = obj,
|
||
Name = p.Name,
|
||
Type = p.PropertyType,
|
||
Value = p.CanRead ? p.GetValue(obj) : null,
|
||
SetValue = new Action<Object?>(v => p.SetValue(obj,v)),
|
||
};
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 对象是否为null
|
||
/// </summary>
|
||
/// <param name="obj">要测试的对象</param>
|
||
/// <returns>True表示对象为null,否则不为null</returns>
|
||
public static bool IsNull([AllowNull] this object obj) => obj == null;
|
||
|
||
/// <summary>
|
||
/// 对象是否不为null。与IsNull相反
|
||
/// </summary>
|
||
/// <param name="obj">要测试的对象</param>
|
||
/// <returns>True表示对象不为null,否则为null</returns>
|
||
public static bool IsNotNull([AllowNull] this object obj) => !obj.IsNull();
|
||
|
||
/// <summary>
|
||
/// 为对象属性设置属性值
|
||
/// </summary>
|
||
/// <param name="obj">要设置的对象</param>
|
||
/// <param name="getValue">通过属性和原始值获取新值的方法委托</param>
|
||
/// <exception cref="ObjectSetValueException">设置属性值引发的异常</exception>
|
||
public static T SetPropertyValue<T>(this T obj,Func<PropertyInfo,object?,object?> getValue) where T : class {
|
||
foreach(PropertyInfo info in obj.GetType().GetProperties()) {
|
||
if(info.CanWrite && info.CanRead) {
|
||
object? originalVal = info.GetValue(obj);
|
||
var val = getValue(info,originalVal);
|
||
try {
|
||
info.SetValue(obj,val);
|
||
}
|
||
catch(Exception ex) {
|
||
throw new ObjectSetValueException(obj,info,val,ex);
|
||
}
|
||
}
|
||
}
|
||
return obj;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将对象转换为Json字符串格式
|
||
/// </summary>
|
||
/// <typeparam name="T">对象的类型</typeparam>
|
||
/// <param name="obj">对象</param>
|
||
/// <param name="OptionBuilder">转换参数</param>
|
||
/// <returns>转换后字符串</returns>
|
||
public static string ToJson<T>(this T obj,Action<JsonSerializerOptions>? OptionBuilder = null) {
|
||
var option = new JsonSerializerOptions();
|
||
OptionBuilder?.Invoke(option);
|
||
var ser = new JsonSerialize.JsonSerializeFactory().CreateJsonSerialize(option);
|
||
return ser.Serialize(obj);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前方法开始向上的调用堆栈。
|
||
/// </summary>
|
||
/// <param name="_">一个对象。可以为任何对象,没有实际意义,可以直接使用this</param>
|
||
/// <param name="startLevel">开始级别。0为当前方法。默认0</param>
|
||
/// <param name="full">是否需要完整调用堆栈</param>
|
||
/// <returns>方法的调用堆栈枚举</returns>
|
||
public static IEnumerable<MethodBase> GetStackTrace(this object _,int startLevel = 0,bool full = false) {
|
||
var trace = new StackTrace();
|
||
var r = new List<MethodBase>();
|
||
for(int i = startLevel;i < trace.FrameCount;i++) {
|
||
var frame = trace.GetFrame(i);
|
||
if(frame == null) {
|
||
break;
|
||
}
|
||
if(!full && frame.GetILOffset() == StackFrame.OFFSET_UNKNOWN) {
|
||
break;
|
||
}
|
||
var method = frame.GetMethod();
|
||
if(method == null) {
|
||
break;
|
||
}
|
||
r.Add(method);
|
||
}
|
||
return r;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 为对象设置测试值。
|
||
/// </summary>
|
||
/// <typeparam name="T">对象的类型</typeparam>
|
||
/// <param name="source">原始对象,可以为null</param>
|
||
/// <param name="targetBuilder">可选,为目标对象复制</param>
|
||
/// <returns>设置了测试值的对象</returns>
|
||
public static T? ToTest<T>(this T? source,Action<T>? targetBuilder = null) where T : class, new() {
|
||
var type = typeof(T);
|
||
var typeName = type.FullName;
|
||
if(typeName.IsNullOrEmpty()) {
|
||
throw new Exception("typeof(T).FullName is null,can not createInstance!");
|
||
}
|
||
var obj = type.Assembly.CreateInstance(typeName) as T ?? throw new Exception("创建目标对象失败!");
|
||
foreach(PropertyInfo p in type.GetProperties()) {
|
||
if(!p.CanWrite || !p.CanRead) {
|
||
continue;
|
||
}
|
||
//如果属性为字符串
|
||
if(p.PropertyType == typeof(string)) {
|
||
var val = source == null ? null : p.GetValue(source) as string;
|
||
if(val.IsNullOrEmpty()) {
|
||
p.SetValue(obj,p.Name);
|
||
}
|
||
else {
|
||
p.SetValue(obj,val);
|
||
}
|
||
continue;
|
||
}
|
||
//其他类型属性不做处理
|
||
}
|
||
targetBuilder?.Invoke(obj);
|
||
return obj as T;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 展开的属性信息
|
||
/// </summary>
|
||
public class ExpandPropertyInfo
|
||
{
|
||
/// <summary>
|
||
/// 目标对象
|
||
/// </summary>
|
||
public object TargetObj { get; set; }
|
||
/// <summary>
|
||
/// 属性名
|
||
/// </summary>
|
||
public string Name { get; set; }
|
||
/// <summary>
|
||
/// 属性类型
|
||
/// </summary>
|
||
public Type Type { get; set; }
|
||
/// <summary>
|
||
/// 属性值
|
||
/// </summary>
|
||
public object? Value { get; set; }
|
||
/// <summary>
|
||
/// 设置对象值
|
||
/// </summary>
|
||
public Action<object?> SetValue { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 为目标属性设置值时引发的异常
|
||
/// </summary>
|
||
public class ObjectSetValueException:Exception
|
||
{
|
||
/// <summary>
|
||
/// 为目标属性设置值时引发的异常
|
||
/// </summary>
|
||
/// <param name="obj">目标对象</param>
|
||
/// <param name="info">属性</param>
|
||
/// <param name="val">目标属性值</param>
|
||
/// <param name="innExceprtion">内部异常</param>
|
||
public ObjectSetValueException(object? obj,PropertyInfo? info,object? val,Exception innExceprtion)
|
||
: base("为目标属性设置值时引发的异常",innExceprtion) {
|
||
this.Obj = obj;
|
||
this.Property = info;
|
||
this.Value = val;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 目标对象
|
||
/// </summary>
|
||
public object? Obj { get; set; }
|
||
/// <summary>
|
||
/// 目标属性
|
||
/// </summary>
|
||
public PropertyInfo? Property { get; set; }
|
||
/// <summary>
|
||
/// 目标属性值
|
||
/// </summary>
|
||
public object? Value { get; set; }
|
||
}
|
||
|
||
}
|