You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# LINQ中字符串与Object类型比较的异常行为解析请求

C# Object类型属性字符串比较异常解析

问题场景

创建Test类,其Name属性为object类型,通过直接赋值、JSON反序列化、克隆、反射创建等方式生成多个实例后,使用LINQ的Where(a=>a.Name=="test")筛选时,仅直接赋值、克隆直接赋值实例、反射创建的实例能匹配;JSON反序列化及克隆它的实例虽显示Name值为"test",却无法匹配,最终匹配数为4(预期6个)。

示例代码

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace TestJsonDeser
{
    internal class Program
    {
        public class Test
        {
            public object Name { get; set; }
            public object Value { get; set; }
        }
        static void Main(string[] args)
        {
            Test test0 = new Test()
            {
                Name = "test",
                Value = 1
            };

            var ts = JsonConvert.SerializeObject(test0);
            var test1 = JsonConvert.DeserializeObject<Test>(ts);

            var test2 = Clone(test0);
            var test3 = Clone(test1);
            var test4 = CreateTest();
            var test5 = Clone(test4);

            List<Test> list = new List<Test>() { test0, test1, test2, test3, test4, test5 };
            var r3 = list.Where(a=>a.Name=="test").ToList();
            Console.WriteLine($"Count:{r3.Count}");
            foreach (var r in r3) Console.WriteLine($"Idx:{list.IndexOf(r)}");
            Console.ReadKey();
        }

        public static T Clone<T>(T source)
        where T : class, new()
        {
            if (source == null)
                return null;
            var tp = typeof(T);
            var ret = Activator.CreateInstance(typeof(T));
            foreach (var prop in tp.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(a => a.CanWrite && a.CanRead))
            {
                var value = prop.GetValue(source, null);
                if (value is string str)
                    prop.GetSetMethod().Invoke(ret, new object[] { str.Clone() });
                if (value is long ln)
                    prop.GetSetMethod().Invoke(ret, new object[] { ln });
            }
            return (T)ret;
        }
        static Test CreateTest()
        {
            var test = Activator.CreateInstance(typeof(Test));
            typeof(Test).GetProperty("Name").SetValue(test, "test", null);
            typeof(Test).GetProperty("Value").SetValue(test, 1, null);
            return (Test)test;
        }
    }
}

输出结果

Count:4
Idx:0
Idx:2
Idx:4
Idx:5

原因分析

这不是LINQ或Newtonsoft.Json的Bug,而是C#类型系统和JSON序列化特性共同作用的结果:

  • 直接赋值/反射创建的实例:Name属性存储的是string类型实例,加上C#字符串驻留机制,"test"是同一个引用。==对引用类型默认比较引用地址,因此能匹配。
  • JSON反序列化的实例:Newtonsoft.Json默认将JSON字符串反序列化为JValue(属于JToken家族)而非原生string类型。虽然JValue.ToString()输出是"test",但实际类型不是string。此时a.Name=="test"是用JValue实例和string实例做引用比较,自然不相等,无法匹配。
  • 克隆JSON反序列化实例:Clone方法仅对string类型做克隆,而test1.Name是JValue,不满足value is string str的判断,克隆时直接复制了JValue的引用,同样无法通过==和字符串"test"匹配。

解决方案

可采用以下任意一种方式修复:

  • 使用Equals做值比较:替换筛选条件为a.Name.Equals("test"),Equals会调用对应类型的重写方法,JValue和string的Equals都会做值比较。
  • 转换为string后比较:a.Name?.ToString() == "test",确保两边都是string类型,此时==会做值比较。
  • 配置JSON序列化行为:通过自定义设置让Newtonsoft.Json将JSON字符串反序列化为原生string类型,例如:
    var settings = new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.All
    };
    var test1 = JsonConvert.DeserializeObject<Test>(ts, settings);
    
    注:TypeNameHandling会在JSON中添加类型信息,仅在可控场景下使用,避免安全风险。

内容的提问来源于stack exchange,提问作者Cirrosi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 17:02:44