下面给你整理一篇 非常实用、深入但易懂、可直接用于博客 / 技术文档的文章:
📌《.NET 反射中的类型不匹配问题解决方案(long 与 Int64 冲突)》
🚀 .NET 反射中的类型不匹配问题解决方案(long 与 Int64 冲突)
在 .NET 中,long 是 System.Int64 的别名,两者本质上是 完全相同的类型。
但在使用反射时,开发者经常会遇到:
- 类型不匹配(Type mismatch)
- 反射赋值时报错
- MethodInfo.Invoke 找不到合适的方法
- PropertyInfo.SetValue 抛出 ArgumentException
典型报错示例:
Object of type 'System.Int64' cannot be converted to type 'System.Int64'
看起来像谜语——明明是同一个类型却不能赋!
本文将对这个诡异问题做系统总结,并给出实际可用的解决方案。
1️⃣ long / Int64 是什么关系?
在 C# 中:
long === System.Int64
ulong === System.UInt64
int === System.Int32
它们是 完全等价的类型别名。
那为什么还会反射类型不匹配?
2️⃣ 常见触发场景(你一定遇到过)
✔ 场景 1:反射设置属性 SetValue
var prop = obj.GetType().GetProperty("Id");
prop.SetValue(obj, 123L);
但如果属性类型是:
public long Id { get; set; }
有时却抛异常:
Object of type 'System.Int64' cannot be converted to type 'System.Int64'
原因是 Nullable、boxing、实际类型并不完全一致。
✔ 场景 2:反射调用方法 Invoke
例如有方法:
public void SetAge(long age)
但你这么调用:
methodInfo.Invoke(obj, new object[]{ 123 });
因为 123 是 int(Int32),导致不匹配:
TargetParameterCountException / ArgumentException
✔ 场景 3:动态对象 + JSON 反序列化
例如:
var value = JObject.Parse("{\"id\":123}")["id"];
value.Type = Int64 // JValue 返回的是 long
但模型是 int Id,也会 mismatch。
3️⃣ 核心原因总结(非常关键)
🔥 反射不会进行隐式类型转换
例如:
int → long在 C# 可自动转换- 但 反射中不会自动转换
- 你必须自己手动处理
🔥 Nullable 与 long 不等价
typeof(long) ≠ typeof(long?)
用户若传:
123L // long
但属性是:
long?
反射也会 mismatch。
🔥 反射 + object 装箱,类型可能为 Int32 而非 Int64
例如:
object x = 123; // Int32
但你要设置的是 long → mismatch
4️⃣ 实战解决方案(最推荐)
✅ 方法 1:使用 Convert.ChangeType(最通用)
var targetType = prop.PropertyType;
var safeValue = Convert.ChangeType(value, Nullable.GetUnderlyingType(targetType) ?? targetType);
prop.SetValue(obj, safeValue);
可同时处理:
- int → long
- long → long?
- string → int
- string → long
- double → decimal
- 等等…
这是 最佳通用做法。
✅ 方法 2:处理 Nullable 类型
必写:
Type finalType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
这是避免 mismatch 的关键步骤之一。
✅ 方法 3:统一所有数值类型为 long 来处理
如果你的反射只处理数值类型:
if (value is IConvertible)
{
value = Convert.ChangeType(value, finalType);
}
✅ 方法 4:MethodInfo.Invoke 前做类型预处理
var parameters = methodInfo.GetParameters();
var newArgs = new object[args.Length];
for(int i = 0; i < args.Length; i++)
{
var pType = parameters[i].ParameterType;
newArgs[i] = Convert.ChangeType(args[i], Nullable.GetUnderlyingType(pType) ?? pType);
}
methodInfo.Invoke(obj, newArgs);
这可以 100% 避免 “参数类型不匹配”。
5️⃣ 实例:完美解决 long / Int64 mismatch
模型
public class User
{
public long Id { get; set; }
public long? Code { get; set; }
}
设置值(最终解决方案)
var user = new User();
var prop = user.GetType().GetProperty("Id");
object value = 123; // Int32
Type finalType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
object safeValue = Convert.ChangeType(value, finalType);
prop.SetValue(user, safeValue);
Console.WriteLine(user.Id); // 输出 123
无任何报错。
6️⃣ 高级技巧:避免 JSON long → int mismatch
如果 JSON 数据里含 long,但你模型是 int,会溢出。
推荐统一处理:
options.JsonSerializerOptions.NumberHandling =
JsonNumberHandling.AllowReadingFromString |
JsonNumberHandling.WriteAsString;
或在反射时 GET 到 long → 自动转 int。
7️⃣ 最佳实践总结(收藏版)
| 推荐项 | 说明 |
|---|---|
✔ 反射前统一类型 finalType = Nullable.GetUnderlyingType(...) | 必须 |
| ✔ 一律用 Convert.ChangeType 转换 | 最稳 |
| ✔ 所有数值类型先转成 IConvertible | 避免装箱问题 |
| ✔ 对 MethodInfo.Invoke 进行参数类型预处理 | 否则类型一定 mismatch |
| ✔ 不依赖 long 与 Int64 的语法糖别名 | 在反射中没有意义 |
一句话总结:
反射不会自动做隐式类型转换,所有类型转换都必须你来手动做。
发表回复