Files
RedHotRoast-ios/Assets/SGModule/Common/SGModule/Scripts/Extensions/ObjectExtensions.cs
T

303 lines
14 KiB
C#
Raw Normal View History

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Text;
using Newtonsoft.Json;
using SGModule.Common.Helper;
namespace SGModule.Common.Extensions {
/// <summary>
/// 提供通用对象类型转换的扩展方法,支持数值类型、字符串、枚举、集合、时间等多种类型间的智能转换。
/// </summary>
public static class ObjectExtensions {
/// <summary>
/// 将对象强制转换为目标类型 <typeparamref name="T" />,转换失败时返回默认值。
/// </summary>
/// <typeparam name="T">目标类型</typeparam>
/// <param name="value">待转换的对象</param>
/// <param name="defaultValue">转换失败时返回的默认值</param>
/// <param name="format">可选格式字符串(如用于时间或数值格式化)</param>
/// <param name="formatProvider">格式化提供者,默认为当前区域设置</param>
/// <returns>转换后的目标类型值,或 <paramref name="defaultValue" />。</returns>
/// <example>
/// 字符串到基本类型
/// <code>
/// "123".As&lt;int&gt;(); // 输出 123
/// "45.67".As&lt;float&gt;(); // 输出 45.67f
/// "true".As&lt;bool&gt;(); // 输出 true
/// "2023-01-01".As&lt;DateTime&gt;(); // 输出 2023-01-01
/// "".As&lt;int&gt;(); // 输出 0
/// ((string)null).As&lt;int&gt;(); // 输出 0
/// "abc".As&lt;int&gt;(); // 输出 0(无法转换)
/// "1.23E+5".As&lt;float&gt;(); // 输出 123000f
/// .....
/// </code>
/// </example>
public static T As<T>(this object value, T defaultValue = default, string format = null, IFormatProvider formatProvider = null) {
return (T) As(value, typeof(T), defaultValue, format, formatProvider);
}
/// <summary>
/// 将对象转换为指定的目标类型。
/// 支持基础类型转换、字符串与 JSON 互转、枚举解析、集合映射、Base64 处理等。
/// </summary>
/// <param name="value">要转换的对象</param>
/// <param name="targetType">目标类型</param>
/// <param name="defaultValue">转换失败时的返回值</param>
/// <param name="format">可选的格式字符串(用于时间、数字格式化)</param>
/// <param name="formatProvider">格式提供者(区域信息),默认为当前区域</param>
/// <returns>转换结果或默认值</returns>
public static object As(this object value, Type targetType, object defaultValue = null, string format = null, IFormatProvider formatProvider = null) {
if (value == null || value == DBNull.Value) {
return defaultValue;
}
formatProvider ??= CultureInfo.CurrentCulture;
try {
// 1. 类型兼容直接返回
if (targetType.IsInstanceOfType(value)) {
return value;
}
// 2. 数值类型之间转换(int, float, decimal 等)
if (value.IsNumericType() && targetType.IsNumericType()) {
return Convert.ChangeType(value, targetType, formatProvider);
}
// 3. 字符串 ⇄ 字节数组
if (targetType == typeof(byte[]) && value is string str) {
return Encoding.UTF8.GetBytes(str);
}
if (targetType == typeof(string) && value is byte[] bytes) {
return Encoding.UTF8.GetString(bytes);
}
// 4. 转为字符串
if (targetType == typeof(string)) {
if (value is string s) {
return s;
}
if (value.IsNumericType()) {
return string.Format(formatProvider, format ?? "{0}", value);
}
if (value is DateTime dt) {
return string.IsNullOrEmpty(format) ? dt.ToString(formatProvider) : dt.ToString(format, formatProvider);
}
try {
return JsonConvert.SerializeObject(value);
}
catch {
return defaultValue;
}
}
// 5. 从字符串解析为目标类型
if (value is string strValue) {
if (string.IsNullOrWhiteSpace(strValue)) {
return defaultValue;
}
// 字符串 → 枚举
if (targetType.IsEnum) {
try {
return Enum.Parse(targetType, strValue, true);
}
catch {
return defaultValue;
}
}
// 字符串 → 数值
if (targetType.IsNumericType()) {
return Convert.ChangeType(strValue, targetType, formatProvider);
}
// 字符串 → 时间
if (targetType == typeof(DateTime)) {
return string.IsNullOrEmpty(format)
? DateTime.Parse(strValue, formatProvider)
: DateTime.ParseExact(strValue, format, formatProvider);
}
// 字符串 → 自定义类(JSON
if (targetType.IsClass && targetType != typeof(string)) {
try {
return JsonConvert.DeserializeObject(strValue, targetType);
}
catch {
return defaultValue;
}
}
}
// 6. 集合类型转换(如 List<string> -> List<int>
if (targetType.IsGenericListOrArray() && value is IEnumerable enumerable) {
var elementType = targetType.GetElementType() ?? targetType.GetGenericArguments()[0];
var list = (IList) Activator.CreateInstance(typeof(List<>).MakeGenericType(elementType));
foreach (var item in enumerable) {
var converted = typeof(ObjectExtensions)
.GetMethod("As", new[] { typeof(object), typeof(Type), typeof(object), typeof(string), typeof(IFormatProvider) })
?.Invoke(null, new[] { item, elementType, null, format, formatProvider });
list.Add(converted);
}
return targetType.IsArray ? list.ToArray(elementType) : list;
}
// 7. 尝试通过 TypeConverter 转换(主要处理结构体、基础类型)
if (targetType.IsValueType || targetType == typeof(string)) {
var strVal = value.ToString();
if (!string.IsNullOrWhiteSpace(strVal)) {
var converter = TypeDescriptor.GetConverter(targetType);
if (converter.CanConvertFrom(typeof(string))) {
return converter.ConvertFrom(null, formatProvider as CultureInfo, strVal);
}
}
}
// 8. Fallback:通过 JSON 转换
return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(value), targetType);
}
catch {
return defaultValue;
}
}
#region 🔧 辅助方法
private static bool IsNumericType(this Type type) {
return Type.GetTypeCode(type) switch {
TypeCode.Byte or TypeCode.SByte or TypeCode.UInt16 or TypeCode.UInt32 or
TypeCode.UInt64 or TypeCode.Int16 or TypeCode.Int32 or TypeCode.Int64 or
TypeCode.Decimal or TypeCode.Double or TypeCode.Single => true,
_ => false
};
}
private static bool IsNumericType(this object value) {
return value?.GetType().IsNumericType() ?? false;
}
private static bool IsGenericListOrArray(this Type type) {
return type != null && (type.IsArray ||
(type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(List<>)
|| type.GetGenericTypeDefinition() == typeof(IList<>))));
}
private static Array ToArray(this IList list, Type elementType) {
var array = Array.CreateInstance(elementType, list.Count);
list.CopyTo(array, 0);
return array;
}
#endregion
}
public static class ObjectExtensionsTest {
public static void AsTestRun() {
TestCase("int 到 int", 123.As<int>(), 123);
TestCase("float 到 int", 123.1f.As<int>(), 123);
TestCase("double 到 decimal", 99.99.As<decimal>(), 99.99m);
TestCase("int 到 long", 100.As<long>(), 100L);
TestCase("字符串 到 int", "123".As<int>(), 123);
TestCase("字符串 到 float", "45.67".As<float>(), 45.67f);
TestCase("字符串 到 bool", "true".As<bool>(), true);
TestCase("字符串 到 DateTime", "2023-01-01".As<DateTime>(), new DateTime(2023, 1, 1));
TestCase("空字符串 到 int", "".As<int>(), 0);
TestCase("null字符串 到 int", ((string) null).As<int>(), 0);
TestCase("非法字符串 到 int", "abc".As<int>(), 0);
TestCase("科学计数法字符串 到 float", "1.23E+5".As<float>(), 123000f);
TestCase("int 到 字符串", 42.As<string>(), "42");
TestCase("float 到 字符串(en-US", 3.14159f.As<string>(formatProvider: new CultureInfo("en-US")), "3.14159");
TestCase("float 到 字符串(de-DE", 3.14159f.As<string>(formatProvider: new CultureInfo("de-DE")), "3,14159");
TestCase("DateTime 到 字符串(格式化)", new DateTime(2024, 5, 1).As<string>(format: "yyyy-MM-dd"), "2024-05-01");
TestCase("null 到 int", ((object) null).As<int>(), 0);
TestCase("null 到 string", ((object) null).As<string>(), null);
TestCase("DBNull 到 int", DBNull.Value.As<int>(), 0);
TestCase("枚举 到 string", Color.Red.As<string>(), "Red");
TestCase("字符串 到 枚举", "Green".As<Color>(), Color.Green);
var json = "{\"Name\":\"Alice\",\"Age\":30}";
TestCase("JSON 字符串 到 Person", json.As<Person>().Name, "Alice");
TestCase("对象 到 JSON 字符串", new Person { Name = "Alice", Age = 30 }.As<string>(), JsonConvert.SerializeObject(new Person { Name = "Alice", Age = 30 }));
TestCase("字符串数组 到 List<int>", new[] { "1", "2", "3" }.As<List<int>>(), new List<int> { 1, 2, 3 });
TestCase("List<float> 到 float[]", new List<float> { 1.1f, 2.2f }.As<float[]>(), new[] { 1.1f, 2.2f });
TestCase("空集合 到 int[]", new string[] { }.As<int[]>(), new int[] { });
var str = "hello";
var bytes = Encoding.UTF8.GetBytes(str);
TestCase("byte[] 到 base64 string", bytes.As<string>(), str);
TestCase("base64 string 到 byte[]", BitConverter.ToString(str.As<byte[]>()), BitConverter.ToString(bytes));
TestCase("字符串小数(德语) 到 float", "1,23".As<float>(formatProvider: new CultureInfo("de-DE")), 1.23f);
TestCase("字符串小数(英语) 到 float", "1.23".As<float>(formatProvider: new CultureInfo("en-US")), 1.23f);
TestCase("非法 JSON 到 Person(失败返回 null", "{invalid}".As<Person>(), null);
TestCase("不能转的对象 到 int", new Person { Name = "A" }.As<int>(), 0);
TestCase("不能转的对象 到 stringfallback 到 JsonConvert", new Person { Name = "A", Age = 20 }.As<string>(), JsonConvert.SerializeObject(new Person { Name = "A", Age = 20 }));
}
private static void TestCase<T>(string description, T actual, T expected) {
var isSuccess = CommonUtils.DeepEquals(actual, expected);
var actualStr = FormatValue(actual);
var expectedStr = FormatValue(expected);
var msg = $"{description} => 结果: {actualStr}, 期望: {expectedStr}";
if (isSuccess) {
Log.Info("As<T>()", msg);
}
else {
Log.Warning("As<T>()", msg);
}
}
private static string FormatValue<T>(T value) {
if (value == null) {
return "null";
}
if (value is byte[] byteArray) {
return $"[{string.Join(", ", byteArray)}]";
}
if (value is int[] intArray) {
return $"[{string.Join(", ", intArray)}]";
}
return value.ToString();
}
private enum Color {
Red,
Green,
Blue
}
private class Person {
public string Name {
get;
set;
}
public int Age {
get;
set;
}
}
}
}