gpt4 book ai didi

c# - 如何使用 Newtonsoft.Json 序列化 C# ref 字段?

转载 作者:行者123 更新时间:2023-11-30 16:41:51 27 4
gpt4 key购买 nike

假设我有这样一个类:

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

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

Note: this class is a stripped-down example, in my real world scenario that _SomeField field is not a member field, otherwise I'd just have added the Json attribute over it. That field is a field exposed by another object which is a member of the class. The public property is just exposing that field to the user, to make it easier to access that value.

Also, the actual property tipe is not int, but a 12-bytes struct, so I'm returning it by reference in order to avoid the useless copy by value.

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

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

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

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

感谢您的帮助!

最佳答案

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

问题出现在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);
}
}
}

关于c# - 如何使用 Newtonsoft.Json 序列化 C# ref 字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47952489/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com