in src/StreamJsonRpc/MessagePackFormatter.cs [415:514]
private static (IReadOnlyDictionary<string, object?> ArgumentValues, IReadOnlyDictionary<string, Type> ArgumentTypes)? GetParamsObjectDictionary(object? paramsObject)
{
if (paramsObject is null)
{
return default;
}
// Look up the argument types dictionary if we saved it before.
Type paramsObjectType = paramsObject.GetType();
IReadOnlyDictionary<string, Type>? argumentTypes;
lock (ParameterObjectPropertyTypes)
{
ParameterObjectPropertyTypes.TryGetValue(paramsObjectType, out argumentTypes);
}
// If we couldn't find a previously created argument types dictionary, create a mutable one that we'll build this time.
Dictionary<string, Type>? mutableArgumentTypes = argumentTypes is null ? new Dictionary<string, Type>() : null;
var result = new Dictionary<string, object?>(StringComparer.Ordinal);
TypeInfo paramsTypeInfo = paramsObject.GetType().GetTypeInfo();
bool isDataContract = paramsTypeInfo.GetCustomAttribute<DataContractAttribute>() is not null;
BindingFlags bindingFlags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance;
if (isDataContract)
{
bindingFlags |= BindingFlags.NonPublic;
}
bool TryGetSerializationInfo(MemberInfo memberInfo, out string key)
{
key = memberInfo.Name;
if (isDataContract)
{
DataMemberAttribute? dataMemberAttribute = memberInfo.GetCustomAttribute<DataMemberAttribute>();
if (dataMemberAttribute is null)
{
return false;
}
if (!dataMemberAttribute.EmitDefaultValue)
{
throw new NotSupportedException($"(DataMemberAttribute.EmitDefaultValue == false) is not supported but was found on: {memberInfo.DeclaringType!.FullName}.{memberInfo.Name}.");
}
key = dataMemberAttribute.Name ?? memberInfo.Name;
return true;
}
else
{
return memberInfo.GetCustomAttribute<IgnoreDataMemberAttribute>() is null;
}
}
foreach (PropertyInfo property in paramsTypeInfo.GetProperties(bindingFlags))
{
if (property.GetMethod is not null)
{
if (TryGetSerializationInfo(property, out string key))
{
result[key] = property.GetValue(paramsObject);
if (mutableArgumentTypes is object)
{
mutableArgumentTypes[key] = property.PropertyType;
}
}
}
}
foreach (FieldInfo field in paramsTypeInfo.GetFields(bindingFlags))
{
if (TryGetSerializationInfo(field, out string key))
{
result[key] = field.GetValue(paramsObject);
if (mutableArgumentTypes is object)
{
mutableArgumentTypes[key] = field.FieldType;
}
}
}
// If we assembled the argument types dictionary this time, save it for next time.
if (mutableArgumentTypes is object)
{
lock (ParameterObjectPropertyTypes)
{
if (ParameterObjectPropertyTypes.TryGetValue(paramsObjectType, out IReadOnlyDictionary<string, Type>? lostRace))
{
// Of the two, pick the winner to use ourselves so we consolidate on one and allow the GC to collect the loser sooner.
argumentTypes = lostRace;
}
else
{
ParameterObjectPropertyTypes.Add(paramsObjectType, argumentTypes = mutableArgumentTypes);
}
}
}
return (result, argumentTypes!);
}