I've been experiencing some peculiar behavior in my C# code that I'm struggling to understand. I'm hoping someone might be able to shed some light on it.
I've created a simple class and list of objects from that class where a string-object comparison is being performed. However, the output seems to suggest that a string-object comparison behaves differently depending on whether the object was directly assigned, cloned, or deserialized from a JSON string.
Here is a simplified example:
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;
}
}
}
The output is:
Count:4
Idx:0
Idx:2
Idx:4
Idx:5
I would expect that all the Test objects would have the Name property equal to "test", but it seems that only the ones that were directly assigned, cloned from the directly assigned, or created with Activator.CreateInstance are considered as having Name equal to "test".
The Test objects that were deserialized from a JSON string or cloned from the deserialized do not match, even though printing the Name property clearly shows the value is "test".
Could anyone help explain why this is happening? Is this a bug in LINQ or Newtonsoft's JSON deserialization, or is it some language feature that I'm not aware of?
Any help would be appreciated.