Ответ 1
Json.NET кэширует информацию о сериализации типов внутри своих IContractResolver
классов DefaultContractResolver
и CamelCasePropertyNamesContractResolver
. Если вы не укажете пользовательский обработчик контракта, эта информация кэшируется и используется повторно.
Для DefaultContractResolver
поддерживается глобальный статический экземпляр, который Json.NET использует всякий раз, когда приложение не указывает свой собственный преобразователь контрактов. CamelCasePropertyNamesContractResolver
, с другой стороны, поддерживает статические таблицы, которые являются общими для всех экземпляров. (Я полагаю, что несоответствие обусловлено устаревшими проблемами; подробности см. в здесь.)
Оба эти типа предназначены для обеспечения полной безопасности потоков, поэтому совместное использование потоков не должно быть проблемой.
Если вы решите создать собственный обработчик контрактов, то информация о типах будет кэшироваться и использоваться повторно только в том случае, если вы кэшируете и повторно используете сам экземпляр распознавателя контрактов. Таким образом, Newtonsoft рекомендует:
Для производительности вы должны создать определитель контракта один раз и повторно использовать экземпляры, когда это возможно. Разрешение контрактов выполняется медленно, и реализации IContractResolver обычно кэшируют контракты.
Одна из стратегий обеспечения кэширования в подклассе DefaultContractResolver
заключается в том, чтобы сделать его конструктор защищенным или закрытым и предоставить глобальный статический экземпляр. (Конечно, это уместно только в том случае, если распознаватель "не имеет состояния" и всегда будет возвращать одинаковые результаты.) Например, вдохновленный этим вопросом, здесь приведен случай, когда необходимо подчеркнуть решатель контрактов:
public class PascalCaseToUnderscoreContractResolver : DefaultContractResolver
{
protected PascalCaseToUnderscoreContractResolver() : base() { }
// As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
// http://www.newtonsoft.com/json/help/html/ContractResolver.htm
// http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
// "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
static PascalCaseToUnderscoreContractResolver instance;
// Using an explicit static constructor enables lazy initialization.
static PascalCaseToUnderscoreContractResolver() { instance = new PascalCaseToUnderscoreContractResolver(); }
public static PascalCaseToUnderscoreContractResolver Instance { get { return instance; } }
static string PascalCaseToUnderscore(string name)
{
if (name == null || name.Length < 1)
return name;
var sb = new StringBuilder(name);
for (int i = 0; i < sb.Length; i++)
{
var ch = char.ToLowerInvariant(sb[i]);
if (ch != sb[i])
{
if (i > 0) // Handle flag delimiters
{
sb.Insert(i, '_');
i++;
}
sb[i] = ch;
}
}
return sb.ToString();
}
protected override string ResolvePropertyName(string propertyName)
{
return PascalCaseToUnderscore(propertyName);
}
}
Что бы вы использовали, как:
var json = JsonConvert.SerializeObject(someObject, new JsonSerializerSettings { ContractResolver = PascalCaseToUnderscoreContractResolver.Instance });
(N.B. - полезность этого конкретного преобразователя была уменьшена с введением SnakeCaseNamingStrategy
. Он был оставлен только в качестве иллюстративного примера.)
Если потребление памяти является проблемой и по какой-либо причине вам необходимо минимизировать объем памяти, постоянно занимаемый кэшированными контрактами, вы можете создать свой собственный локальный экземпляр DefaultContractResolver
(или некоторый пользовательский подкласс), сериализовать его, а затем немедленно удалите все ссылки на него, например:
public class JsonExtensions
{
public static string SerializeObjectNoCache<T>(T obj, JsonSerializerSettings settings = null)
{
settings = settings ?? new JsonSerializerSettings();
if (settings.ContractResolver == null)
// To reduce memory footprint, do not cache contract information in the global contract resolver.
settings.ContractResolver = new DefaultContractResolver();
return JsonConvert.SerializeObject(obj, settings);
}
}
Большая часть кэшированной памяти контрактов со временем будет собираться мусором. Конечно, при этом производительность сериализации может существенно снизиться.
Для получения дополнительной информации см. раздел Советы по повышению производительности: повторное использование контрактного резолвера.