【问题标题】:How to serialize a C# ref field with Newtonsoft.Json?如何使用 Newtonsoft.Json 序列化 C# 引用字段?
【发布时间】:2018-06-05 18:08:22
【问题描述】:

假设我有这样的课程:

[JsonObject(MemberSerialization.OptIn)]
public class TestClass
{
    private readonly int _SomeField;

    [JsonProperty(nameof(InputInfo))]
    public ref readonly int SomeField => ref _SomeField;
}

注意:这个类是一个精简的例子,在我的现实世界场景中,_SomeField 字段不是成员字段,否则我只会有在它上面添加了 Json 属性。该字段是由作为该类成员的另一个对象公开的字段。公共属性只是将该字段公开给用户,以便更轻松地访问该值。

另外,实际的属性tipe 不是 int,而是一个12字节的struct,所以我通过引用返回它以避免按价值计算的无用副本。

我正在使用JsonConvert.SerializeObject(this, Formatting.Indented) 序列化这样一个类。

Newtonsoft.Json 在转换值时抛出异常,说它无法访问字段/属性值(我猜是ref 参数会使库使用的反射过程崩溃)。

我尝试使用自定义 JsonConverter 进行试验,但在使用任何其他转换器之前就发生了崩溃。

我知道一个快速的解决方案是添加一个辅助的私有参数,该参数仅将该字段作为值而不是通过引用返回,并且仅将其用于 Json 序列化,但对我来说看起来很糟糕(我会也禁用有关未使用的私有参数的自动 VS 警告),如果可能的话,我正在寻找更好的解决方案(不引入无用的字段/属性)。

感谢您的帮助!

【问题讨论】:

    标签: c# json serialization json.net ref


    【解决方案1】:

    这篇评论太长了,如果有人发布另一个答案,我会删除它。快速浏览一下,您目前无法覆盖它。

    问题出现在DynamicValueProvider.cs第110行:

    public object GetValue(object target)
    {
            try
            {
                if (_getter == null)
                {
                    _getter = DynamicReflectionDelegateFactory.Instance.CreateGet<object>(_memberInfo);
                }
    
                return _getter(target); //Line 100
            }
            catch (Exception ex)
            {
                throw new JsonSerializationException("Error getting value from '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex);
            }
    }
    

    原因在CreateGet,它无法生成正确处理这些类型的方法。也许你应该在 GitHub 上打开一个新问题(如果还没有的话)。

    您可以在下面看到一个重现该问题的小应用:

    using System;
    using System.Reflection;
    using System.Reflection.Emit;
    
    namespace ConsoleApp15
    {
        public class TestClass
        {
            public TestClass()
            {
                _SomeField = 42;
            }
    
            private readonly int _SomeField;
    
            public ref readonly int SomeField => ref _SomeField;
        }
    
        internal class Program
        {
            private static void Main(string[] args)
            {
                var propertyInfo = typeof(TestClass).GetProperty("SomeField");
                var getMethod = CreateGet<object>(propertyInfo);
    
                TestClass obj = new TestClass();
    
                var result = getMethod(obj);
            }
    
            public static Func<T, object> CreateGet<T>(PropertyInfo propertyInfo)
            {
                DynamicMethod dynamicMethod = CreateDynamicMethod("Get" + propertyInfo.Name, typeof(object), new[] { typeof(T) }, propertyInfo.DeclaringType);
                ILGenerator generator = dynamicMethod.GetILGenerator();
    
                GenerateCreateGetPropertyIL(propertyInfo, generator);
    
                return (Func<T, object>)dynamicMethod.CreateDelegate(typeof(Func<T, object>));
            }
    
            private static DynamicMethod CreateDynamicMethod(string name, Type returnType, Type[] parameterTypes, Type owner)
            {
                DynamicMethod dynamicMethod = new DynamicMethod(name, returnType, parameterTypes, owner, true);
    
                return dynamicMethod;
            }
    
            private static void GenerateCreateGetPropertyIL(PropertyInfo propertyInfo, ILGenerator generator)
            {
                MethodInfo getMethod = propertyInfo.GetGetMethod(true);
                if (getMethod == null)
                {
                    throw new ArgumentException("Property " + propertyInfo.Name + " does not have a getter.");
                }
    
                if (!getMethod.IsStatic)
                {
                    generator.PushInstance(propertyInfo.DeclaringType);
                }
    
                generator.CallMethod(getMethod);
                generator.BoxIfNeeded(propertyInfo.PropertyType);
                generator.Return();
            }
        }
    
        internal static class ILGeneratorExtensions
        {
            public static void PushInstance(this ILGenerator generator, Type type)
            {
                generator.Emit(OpCodes.Ldarg_0);
                if (type.IsValueType)
                {
                    generator.Emit(OpCodes.Unbox, type);
                }
                else
                {
                    generator.Emit(OpCodes.Castclass, type);
                }
            }
    
            public static void PushArrayInstance(this ILGenerator generator, int argsIndex, int arrayIndex)
            {
                generator.Emit(OpCodes.Ldarg, argsIndex);
                generator.Emit(OpCodes.Ldc_I4, arrayIndex);
                generator.Emit(OpCodes.Ldelem_Ref);
            }
    
            public static void BoxIfNeeded(this ILGenerator generator, Type type)
            {
                if (type.IsValueType)
                {
                    generator.Emit(OpCodes.Box, type);
                }
                else
                {
                    generator.Emit(OpCodes.Castclass, type);
                }
            }
    
            public static void UnboxIfNeeded(this ILGenerator generator, Type type)
            {
                if (type.IsValueType)
                {
                    generator.Emit(OpCodes.Unbox_Any, type);
                }
                else
                {
                    generator.Emit(OpCodes.Castclass, type);
                }
            }
    
            public static void CallMethod(this ILGenerator generator, MethodInfo methodInfo)
            {
                if (methodInfo.IsFinal || !methodInfo.IsVirtual)
                {
                    generator.Emit(OpCodes.Call, methodInfo);
                }
                else
                {
                    generator.Emit(OpCodes.Callvirt, methodInfo);
                }
            }
    
            public static void Return(this ILGenerator generator)
            {
                generator.Emit(OpCodes.Ret);
            }
        }
    }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-12-20
    • 1970-01-01
    • 2019-08-12
    • 2017-08-04
    • 2014-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多