下面给你整理一篇 非常实用、深入但易懂、可直接用于博客 / 技术文档的文章
📌《.NET 反射中的类型不匹配问题解决方案(long 与 Int64 冲突)》


🚀 .NET 反射中的类型不匹配问题解决方案(long 与 Int64 冲突)

在 .NET 中,longSystem.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 });

因为 123int(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 的语法糖别名在反射中没有意义

一句话总结:

反射不会自动做隐式类型转换,所有类型转换都必须你来手动做