mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-20 10:50:48 +08:00
507 lines
17 KiB
C#
507 lines
17 KiB
C#
//------------------------------------------------------------------------------
|
||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||
// 使用文档:https://thingsgateway.cn/
|
||
// QQ群:605534569
|
||
//------------------------------------------------------------------------------
|
||
#if NET6_0_OR_GREATER
|
||
|
||
using System.Collections.Concurrent;
|
||
using System.ComponentModel;
|
||
using System.Reflection;
|
||
using System.Runtime.CompilerServices;
|
||
using System.Text.Json;
|
||
using System.Text.RegularExpressions;
|
||
|
||
using ThingsGateway.Extension;
|
||
|
||
namespace ThingsGateway.Common.Extension;
|
||
/// <summary>
|
||
/// 对象拓展类
|
||
/// </summary>
|
||
public static class ObjectExtensions
|
||
{
|
||
/// <summary>
|
||
/// 判断类型是否实现某个泛型
|
||
/// </summary>
|
||
/// <param name="type">类型</param>
|
||
/// <param name="generic">泛型类型</param>
|
||
/// <returns>bool</returns>
|
||
public static bool HasImplementedRawGeneric(this Type type, Type generic)
|
||
{
|
||
// 检查接口类型
|
||
var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType);
|
||
if (isTheRawGenericType) return true;
|
||
|
||
// 检查类型
|
||
while (type != null && type != typeof(object))
|
||
{
|
||
isTheRawGenericType = IsTheRawGenericType(type);
|
||
if (isTheRawGenericType) return true;
|
||
type = type.BaseType!;
|
||
}
|
||
|
||
return false;
|
||
|
||
// 判断逻辑
|
||
bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type);
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 合并两个字典
|
||
/// </summary>
|
||
/// <typeparam name="T"></typeparam>
|
||
/// <param name="dic">字典</param>
|
||
/// <param name="newDic">新字典</param>
|
||
/// <returns></returns>
|
||
internal static Dictionary<string, T> AddOrUpdate<T>(this Dictionary<string, T> dic, IDictionary<string, T> newDic)
|
||
{
|
||
foreach (var key in newDic.Keys)
|
||
{
|
||
if (dic.TryGetValue(key, out var value))
|
||
{
|
||
dic[key] = value;
|
||
}
|
||
else
|
||
{
|
||
dic.Add(key, newDic[key]);
|
||
}
|
||
}
|
||
|
||
return dic;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 合并两个字典
|
||
/// </summary>
|
||
/// <typeparam name="T"></typeparam>
|
||
/// <param name="dic">字典</param>
|
||
/// <param name="newDic">新字典</param>
|
||
internal static void AddOrUpdate<T>(this NonBlockingDictionary<string, T> dic, Dictionary<string, T> newDic)
|
||
{
|
||
foreach (var (key, value) in newDic)
|
||
{
|
||
dic.AddOrUpdate(key, value, (key, old) => value);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将一个对象转换为指定类型
|
||
/// </summary>
|
||
/// <typeparam name="T"></typeparam>
|
||
/// <param name="obj"></param>
|
||
/// <returns></returns>
|
||
internal static T ChangeType<T>(this object obj)
|
||
{
|
||
return (T)ChangeType(obj, typeof(T));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将一个对象转换为指定类型
|
||
/// </summary>
|
||
/// <param name="obj">待转换的对象</param>
|
||
/// <param name="type">目标类型</param>
|
||
/// <returns>转换后的对象</returns>
|
||
public static object? ChangeType(this object? obj, Type type)
|
||
{
|
||
if (type == null) return obj;
|
||
if (type == typeof(string)) return obj?.ToString();
|
||
if (type == typeof(Guid) && obj != null) return Guid.Parse(obj.ToString());
|
||
if (type == typeof(bool) && obj != null && obj is not bool)
|
||
{
|
||
var objStr = obj.ToString().ToLower();
|
||
if (objStr == "1" || objStr == "true" || objStr == "yes" || objStr == "on") return true;
|
||
return false;
|
||
}
|
||
if (obj == null) return type.IsValueType ? Activator.CreateInstance(type) : null;
|
||
|
||
var underlyingType = Nullable.GetUnderlyingType(type);
|
||
if (type.IsAssignableFrom(obj.GetType())) return obj;
|
||
else if ((underlyingType ?? type).IsEnum)
|
||
{
|
||
if (underlyingType != null && string.IsNullOrWhiteSpace(obj.ToString())) return null;
|
||
else return Enum.Parse(underlyingType ?? type, obj.ToString());
|
||
}
|
||
// 处理 DateTime -> DateTimeOffset 类型
|
||
else if (obj.GetType().Equals(typeof(DateTime)) && (underlyingType ?? type).Equals(typeof(DateTimeOffset)))
|
||
{
|
||
return ((DateTime)obj).ConvertToDateTimeOffset();
|
||
}
|
||
// 处理 DateTimeOffset -> DateTime 类型
|
||
else if (obj.GetType().Equals(typeof(DateTimeOffset)) && (underlyingType ?? type).Equals(typeof(DateTime)))
|
||
{
|
||
return ((DateTimeOffset)obj).ConvertToDateTime();
|
||
}
|
||
// 处理 DateTime -> DateOnly 类型
|
||
else if (obj.GetType().Equals(typeof(DateTime)) && (underlyingType ?? type).Equals(typeof(DateOnly)))
|
||
{
|
||
return DateOnly.FromDateTime(((DateTime)obj));
|
||
}
|
||
// 处理 DateTime -> TimeOnly 类型
|
||
else if (obj.GetType().Equals(typeof(DateTime)) && (underlyingType ?? type).Equals(typeof(TimeOnly)))
|
||
{
|
||
return TimeOnly.FromDateTime(((DateTime)obj));
|
||
}
|
||
else if (typeof(IConvertible).IsAssignableFrom(underlyingType ?? type))
|
||
{
|
||
try
|
||
{
|
||
return Convert.ChangeType(obj, underlyingType ?? type, null);
|
||
}
|
||
catch
|
||
{
|
||
return underlyingType == null ? Activator.CreateInstance(type) : null;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
var converter = TypeDescriptor.GetConverter(type);
|
||
if (converter.CanConvertFrom(obj.GetType())) return converter.ConvertFrom(obj);
|
||
|
||
var constructor = type.GetConstructor(Type.EmptyTypes);
|
||
if (constructor != null)
|
||
{
|
||
var o = constructor.Invoke(null);
|
||
var propertys = type.GetProperties();
|
||
var oldType = obj.GetType();
|
||
|
||
foreach (var property in propertys)
|
||
{
|
||
var p = oldType.GetProperty(property.Name);
|
||
if (property.CanWrite && p?.CanRead == true)
|
||
{
|
||
property.SetValue(o, ChangeType(p.GetValue(obj, null), property.PropertyType), null);
|
||
}
|
||
}
|
||
return o;
|
||
}
|
||
}
|
||
|
||
return obj;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除字符串前后缀
|
||
/// </summary>
|
||
/// <param name="str">字符串</param>
|
||
/// <param name="pos">0:前后缀,1:后缀,-1:前缀</param>
|
||
/// <param name="affixes">前后缀集合</param>
|
||
/// <returns></returns>
|
||
internal static string ClearStringAffixes(this string str, int pos = 0, params string[] affixes)
|
||
{
|
||
// 空字符串直接返回
|
||
if (string.IsNullOrWhiteSpace(str)) return str;
|
||
|
||
// 空前后缀集合直接返回
|
||
if (affixes == null || affixes.Length == 0) return str;
|
||
|
||
var startCleared = false;
|
||
var endCleared = false;
|
||
|
||
string tempStr = null;
|
||
foreach (var affix in affixes)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(affix)) continue;
|
||
|
||
if (pos != 1 && !startCleared && str.StartsWith(affix, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
tempStr = str[affix.Length..];
|
||
startCleared = true;
|
||
}
|
||
if (pos != -1 && !endCleared && str.EndsWith(affix, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
var _tempStr = !string.IsNullOrWhiteSpace(tempStr) ? tempStr : str;
|
||
tempStr = _tempStr[..^affix.Length];
|
||
endCleared = true;
|
||
|
||
if (string.IsNullOrWhiteSpace(tempStr))
|
||
{
|
||
tempStr = null;
|
||
endCleared = false;
|
||
}
|
||
}
|
||
if (startCleared && endCleared) break;
|
||
}
|
||
|
||
return !string.IsNullOrWhiteSpace(tempStr) ? tempStr : str;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将时间戳转换为 DateTime
|
||
/// </summary>
|
||
/// <param name="timestamp"></param>
|
||
/// <returns></returns>
|
||
internal static DateTime ConvertToDateTime(this long timestamp)
|
||
{
|
||
var timeStampDateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||
var digitCount = (int)Math.Floor(Math.Log10(timestamp) + 1);
|
||
|
||
if (digitCount != 13 && digitCount != 10)
|
||
{
|
||
throw new ArgumentException("Data is not a valid timestamp format.");
|
||
}
|
||
|
||
return (digitCount == 13
|
||
? timeStampDateTime.AddMilliseconds(timestamp) // 13 位时间戳
|
||
: timeStampDateTime.AddSeconds(timestamp)).ToLocalTime(); // 10 位时间戳
|
||
}
|
||
|
||
/// <summary>
|
||
/// 格式化字符串
|
||
/// </summary>
|
||
/// <param name="str"></param>
|
||
/// <param name="args"></param>
|
||
/// <returns></returns>
|
||
internal static string Format(this string str, params object[] args)
|
||
{
|
||
return args == null || args.Length == 0 ? str : string.Format(str, args);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取所有祖先类型
|
||
/// </summary>
|
||
/// <param name="type"></param>
|
||
/// <returns></returns>
|
||
internal static IEnumerable<Type> GetAncestorTypes(this Type type)
|
||
{
|
||
var ancestorTypes = new List<Type>();
|
||
while (type != null && type != typeof(object))
|
||
{
|
||
if (IsNoObjectBaseType(type))
|
||
{
|
||
var baseType = type.BaseType;
|
||
ancestorTypes.Add(baseType);
|
||
type = baseType;
|
||
}
|
||
else break;
|
||
}
|
||
|
||
return ancestorTypes;
|
||
|
||
static bool IsNoObjectBaseType(Type type) => type.BaseType != typeof(object);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查找方法指定特性,如果没找到则继续查找声明类
|
||
/// </summary>
|
||
/// <typeparam name="TAttribute"></typeparam>
|
||
/// <param name="method"></param>
|
||
/// <param name="inherit"></param>
|
||
/// <returns></returns>
|
||
public static TAttribute GetFoundAttribute<TAttribute>(this MethodInfo method, bool inherit)
|
||
where TAttribute : Attribute
|
||
{
|
||
// 获取方法所在类型
|
||
var declaringType = method.DeclaringType;
|
||
|
||
var attributeType = typeof(TAttribute);
|
||
|
||
// 判断方法是否定义指定特性,如果没有再查找声明类
|
||
var foundAttribute = method.IsDefined(attributeType, inherit)
|
||
? method.GetCustomAttribute<TAttribute>(inherit)
|
||
: (
|
||
declaringType.IsDefined(attributeType, inherit)
|
||
? declaringType.GetCustomAttribute<TAttribute>(inherit)
|
||
: default
|
||
);
|
||
|
||
return foundAttribute;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取方法真实返回类型
|
||
/// </summary>
|
||
/// <param name="method"></param>
|
||
/// <returns></returns>
|
||
internal static Type GetRealReturnType(this MethodInfo method)
|
||
{
|
||
// 判断是否是异步方法
|
||
var isAsyncMethod = method.IsAsync();
|
||
|
||
// 获取类型返回值并处理 Task 和 Task<T> 类型返回值
|
||
var returnType = method.ReturnType;
|
||
return isAsyncMethod ? (returnType.GenericTypeArguments.FirstOrDefault() ?? typeof(void)) : returnType;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取类型自定义特性
|
||
/// </summary>
|
||
/// <typeparam name="TAttribute">特性类型</typeparam>
|
||
/// <param name="type">类类型</param>
|
||
/// <param name="inherit">是否继承查找</param>
|
||
/// <returns>特性对象</returns>
|
||
internal static TAttribute GetTypeAttribute<TAttribute>(this Type type, bool inherit = false)
|
||
where TAttribute : Attribute
|
||
{
|
||
// 空检查
|
||
ArgumentNullException.ThrowIfNull(type);
|
||
|
||
// 检查特性并获取特性对象
|
||
return type.IsDefined(typeof(TAttribute), inherit)
|
||
? type.GetCustomAttribute<TAttribute>(inherit)
|
||
: default;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断是否是匿名类型
|
||
/// </summary>
|
||
/// <param name="obj">对象</param>
|
||
/// <returns></returns>
|
||
internal static bool IsAnonymous(this object obj)
|
||
{
|
||
var type = obj is Type t ? t : obj.GetType();
|
||
|
||
return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false)
|
||
&& type.IsGenericType && type.Name.Contains("AnonymousType")
|
||
&& (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
|
||
&& type.Attributes.HasFlag(TypeAttributes.NotPublic);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断方法是否是异步
|
||
/// </summary>
|
||
/// <param name="method">方法</param>
|
||
/// <returns></returns>
|
||
internal static bool IsAsync(this MethodInfo method)
|
||
{
|
||
return method.GetCustomAttribute<AsyncMethodBuilderAttribute>() != null
|
||
|| method.ReturnType.ToString().StartsWith(typeof(Task).FullName);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断集合是否为空
|
||
/// </summary>
|
||
/// <typeparam name="T">元素类型</typeparam>
|
||
/// <param name="collection">集合对象</param>
|
||
/// <returns><see cref="bool"/> 实例,true 表示空集合,false 表示非空集合</returns>
|
||
internal static bool IsEmpty<T>(this IEnumerable<T> collection)
|
||
{
|
||
return collection?.Any() != true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断是否是富基元类型
|
||
/// </summary>
|
||
/// <param name="type">类型</param>
|
||
/// <returns></returns>
|
||
public static bool IsRichPrimitive(this Type? type)
|
||
{
|
||
if (type == null) return false;
|
||
|
||
// 处理元组类型
|
||
if (type.IsValueTuple()) return false;
|
||
|
||
// 处理数组类型,基元数组类型也可以是基元类型
|
||
if (type.IsArray) return type.GetElementType()?.IsRichPrimitive() ?? false;
|
||
|
||
// 基元类型或值类型或字符串类型
|
||
if (type.IsPrimitive || type.IsValueType || type == typeof(string)) return true;
|
||
|
||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) return type.GenericTypeArguments[0].IsRichPrimitive();
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断是否是元组类型
|
||
/// </summary>
|
||
/// <param name="type">类型</param>
|
||
/// <returns></returns>
|
||
internal static bool IsValueTuple(this Type type)
|
||
{
|
||
return type.Namespace == "System" && type.Name.Contains("ValueTuple`");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 切割骆驼命名式字符串
|
||
/// </summary>
|
||
/// <param name="str"></param>
|
||
/// <returns></returns>
|
||
internal static string[] SplitCamelCase(this string str)
|
||
{
|
||
if (str == null) return Array.Empty<string>();
|
||
|
||
if (string.IsNullOrWhiteSpace(str)) return [str];
|
||
if (str.Length == 1) return [str];
|
||
|
||
return Regex.Split(str, @"(?=\p{Lu}\p{Ll})|(?<=\p{Ll})(?=\p{Lu})")
|
||
.Where(u => u.Length > 0)
|
||
.ToArray();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 首字母小写
|
||
/// </summary>
|
||
/// <param name="str"></param>
|
||
/// <returns></returns>
|
||
internal static string ToLowerCamelCase(this string str)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(str)) return str;
|
||
|
||
return string.Concat(str[0].ToString().ToLower(), str.AsSpan(1));
|
||
}
|
||
|
||
/// <summary>
|
||
/// JsonElement 转 Object
|
||
/// </summary>
|
||
/// <param name="jsonElement"></param>
|
||
/// <returns></returns>
|
||
internal static object ToObject(this JsonElement jsonElement)
|
||
{
|
||
switch (jsonElement.ValueKind)
|
||
{
|
||
case JsonValueKind.String:
|
||
return jsonElement.GetString();
|
||
|
||
case JsonValueKind.Undefined:
|
||
case JsonValueKind.Null:
|
||
return default;
|
||
|
||
case JsonValueKind.Number:
|
||
return jsonElement.GetDecimal();
|
||
|
||
case JsonValueKind.True:
|
||
case JsonValueKind.False:
|
||
return jsonElement.GetBoolean();
|
||
|
||
case JsonValueKind.Object:
|
||
var enumerateObject = jsonElement.EnumerateObject();
|
||
var dic = new Dictionary<string, object>();
|
||
foreach (var item in enumerateObject)
|
||
{
|
||
dic.Add(item.Name, item.Value.ToObject());
|
||
}
|
||
return dic;
|
||
|
||
case JsonValueKind.Array:
|
||
var enumerateArray = jsonElement.EnumerateArray();
|
||
var list = new List<object>();
|
||
foreach (var item in enumerateArray)
|
||
{
|
||
list.Add(item.ToObject());
|
||
}
|
||
return list;
|
||
|
||
default:
|
||
return default;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 首字母大写
|
||
/// </summary>
|
||
/// <param name="str"></param>
|
||
/// <returns></returns>
|
||
internal static string ToUpperCamelCase(this string str)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(str)) return str;
|
||
|
||
return string.Concat(str[0].ToString().ToUpper(), str.AsSpan(1));
|
||
}
|
||
}
|
||
#endif |