创建 Json 对象时反射的性能影响

问题描述 投票:0回答:1

我有下面的代码来准备 logEventInfo 对象来记录数据。我正在使用 Nlog。我发现使用反射动态添加名称和值很方便。但我知道这会对性能产生很大影响。

public static LogEventInfo ToLogEventInfo(this ILogItem data, string message, Exception ex = null)
        {
            var eventInfo = new LogEventInfo();

            if (ex != null)
                eventInfo.Exception = ex;

            if (data != null)
            {
                data.EventMessage = message;
                data.LogTime = TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.Utc);
                data.LogId = Guid.NewGuid();

                var properties = data.GetType().GetProperties();
                foreach (PropertyInfo property in properties) //Possibly a performance impact.
                {
                    eventInfo.Properties[property.Name] = property.GetValue(data, null);
                }
            }
            else
            {
                if (!string.IsNullOrEmpty(message))
                    eventInfo.Message = message;
            }

            return eventInfo;
        }

这个 ToLogEvenInfo 函数将在循环中被调用。将循环的数据可能是数百万。有没有更好的方法来实现以下功能?非常感谢。

c# reflection nlog system.reflection
1个回答
0
投票

注意 NLog Jsonlayout 执行反射没有问题,因此您只需将

ILogItem
添加到 LogEventInfo.Properties,然后将 NLog JsonLayout 与
includeAllProperties="true"
maxRecursionLimit="1"
一起使用。您还可以使用 ${event-properties:item=LogItem:format=@}:

  <layout type="JsonLayout" includeAllProperties="true" maxRecursionLimit="1" excludeProperties="LogItem" >
    <attribute name="Time" layout="${date:format=O}" />
    <attribute name="Level" layout="${level:upperCase=true}"/>
    <attribute name="LogItem" encode="false" layout="${event-properties:item=LogItem:format=@}" />
  </layout>

Microsoft System.Text.Json 序列化程序在处理基本 DTO 类时应该很快。但是当遇到特殊的对象值时可能会在你面前爆炸,因为它期望默认情况下对所有对象进行序列化和反序列化。

但是如果你只是想让反射快一点,那么这应该很管用。

static ConcurrentDictionary<Type, Func<object, IEnumerable<KeyValuePair<string, object>>> _typeProperties = new();

static IEnumerable<KeyValuePair<string, object> ResolveProperties(object value)
{
    if (!_typeProperties.TryGetValue(value.GetType(), out var propertyResolver))
    {
       var properties = value.GetType().GetProperties();

       propertyResolver = (v) => {
          foreach (PropertyInfo property in properties)
          {
             var propertyName = property.Name;
             var propertyValue = property.GetValue(v, null);
             yield new KeyValuePair<string, object>(propertyName, propertyValue);
          }
       _typeProperties.TryAdd(value.GetType(), propertyResolver);
    }
    return propertyResolver.Invoke(this);
}

您可以通过编译

property.GetValue(..)
进一步优化它 表达式树。也许是这样的:


private static Func<object, object> GenerateGetterLambda(PropertyInfo property)
{
    // Define our instance parameter, which will be the input of the Func
    var objParameterExpr = Expression.Parameter(typeof(object), "instance");
    // 1. Cast the instance to the correct type
    var instanceExpr = Expression.TypeAs(objParameterExpr, property.DeclaringType);
    // 2. Call the getter and retrieve the value of the property
    var propertyExpr = Expression.Property(instanceExpr, property);
    // 3. Convert the property's value to object
    var propertyObjExpr = Expression.Convert(propertyExpr, typeof(object));
    // Create a lambda expression of the latest call & compile it
    return Expression.Lambda<Func<object, object>>(propertyObjExpr, objParameterExpr).Compile();
}

static IEnumerable<KeyValuePair<string, object> ResolveProperties(object value)
{
    if (!_typeProperties.TryGetValue(value.GetType(), out var propertyResolver))
    {
       var slowProperties = value.GetType().GetProperties();
       var fastProperties = new Func<object, object>[slowProperties.Length];
       for (int i = 0; i < slowProperties.Length; ++i)
          fastProperties = GenerateGetterLambda(slowProperties[i]);

       propertyResolver = (v) => {
          for (int i = 0; i < slowProperties.Length; ++i)
          {
             var propertyName = slowProperties[i].Name;
             var propertyValue = fastProperties[i].Invoke(v);
             yield new KeyValuePair<string, object>(propertyName, propertyValue);
          }
       _typeProperties.TryAdd(value.GetType(), propertyResolver);
    }
    return propertyResolver.Invoke(this);
}

另见:https://blog.zhaytam.com/2020/11/17/expression-trees-property-getter/

© www.soinside.com 2019 - 2024. All rights reserved.