private static void ConvertToUpper(object entity, Hashtable visited)
    {
        if (entity != null && !visited.ContainsKey(entity))
        {
            visited.Add(entity, entity);

            foreach (PropertyInfo propertyInfo in entity.GetType().GetProperties())
            {
                if (!propertyInfo.CanRead || !propertyInfo.CanWrite)
                    continue;

                object propertyValue = propertyInfo.GetValue(entity, null);

                Type propertyType;
                if ((propertyType = propertyInfo.PropertyType) == typeof(string))
                {
                    if (propertyValue != null && !propertyInfo.Name.Contains("password"))
                    {
                        propertyInfo.SetValue(entity, ((string)propertyValue).ToUpper(), null);
                    }
                    continue;
                }

                if (!propertyType.IsValueType)
                {
                    IEnumerable enumerable;
                    if ((enumerable = propertyValue as IEnumerable) != null)
                    {
                        foreach (object value in enumerable)
                        {
                            ConvertToUpper(value, visited);
                        }
                    }
                    else
                    {
                        ConvertToUpper(propertyValue, visited);
                    }
                }
            }
        }
    }

现在,对于列表相对较小的对象,它可以很好地工作,但是一旦对象列表变大,它将永远存在。我将如何优化它,并设置最大深度的限制。

谢谢你的帮助

最佳答案

我没有分析以下代码,但是它在复杂结构上必须非常出色。

1)使用动态代码生成。

2)对生成的动态委托使用基于类型的缓存。

public class VisitorManager : HashSet<object>
{
  delegate void Visitor(VisitorManager manager, object entity);

  Dictionary<Type, Visitor> _visitors = new Dictionary<Type, Visitor>();

  void ConvertToUpperEnum(IEnumerable entity)
  {
    // TODO: this can be parallelized, but then we should thread-safe lock the cache
    foreach (var obj in entity)
      ConvertToUpper(obj);
  }

  public void ConvertToUpper(object entity)
  {
    if (entity != null && !Contains(entity))
    {
      Add(entity);

      var visitor = GetCachedVisitor(entity.GetType());

      if (visitor != null)
        visitor(this, entity);
    }
  }

  Type _lastType;
  Visitor _lastVisitor;

  Visitor GetCachedVisitor(Type type)
  {
    if (type == _lastType)
      return _lastVisitor;

    _lastType = type;

    return _lastVisitor = GetVisitor(type);
  }

  Visitor GetVisitor(Type type)
  {
    Visitor result;

    if (!_visitors.TryGetValue(type, out result))
      _visitors[type] = result = BuildVisitor(type);

    return result;
  }

  static MethodInfo _toUpper = typeof(string).GetMethod("ToUpper", new Type[0]);
  static MethodInfo _convertToUpper = typeof(VisitorManager).GetMethod("ConvertToUpper", BindingFlags.Instance | BindingFlags.Public);
  static MethodInfo _convertToUpperEnum = typeof(VisitorManager).GetMethod("ConvertToUpperEnum", BindingFlags.Instance | BindingFlags.NonPublic);

  Visitor BuildVisitor(Type type)
  {
    var visitorManager = Expression.Parameter(typeof(VisitorManager), "manager");
    var entityParam = Expression.Parameter(typeof(object), "entity");

    var entityVar = Expression.Variable(type, "e");
    var cast = Expression.Assign(entityVar, Expression.Convert(entityParam, type));  // T e = (T)entity;

    var statements = new List<Expression>() { cast };

    foreach (var prop in type.GetProperties())
    {
      // if cannot read or cannot write - ignore property
      if (!prop.CanRead || !prop.CanWrite) continue;

      var propType = prop.PropertyType;

      // if property is value type - ignore property
      if (propType.IsValueType) continue;

      var isString = propType == typeof(string);

      // if string type but no password in property name - ignore property
      if (isString && !prop.Name.Contains("password"))
        continue;

      #region e.Prop

      var propAccess = Expression.Property(entityVar, prop); // e.Prop

      #endregion

      #region T value = e.Prop

      var value = Expression.Variable(propType, "value");
      var assignValue = Expression.Assign(value, propAccess);

      #endregion

      if (isString)
      {
        #region if (value != null) e.Prop = value.ToUpper();

        var ifThen = Expression.IfThen(Expression.NotEqual(value, Expression.Constant(null, typeof(string))),
           Expression.Assign(propAccess, Expression.Call(value, _toUpper)));

        #endregion

        statements.Add(Expression.Block(new[] { value }, assignValue, ifThen));
      }
      else
      {
        #region var i = value as IEnumerable;

        var enumerable = Expression.Variable(typeof(IEnumerable), "i");

        var assignEnum = Expression.Assign(enumerable, Expression.TypeAs(value, enumerable.Type));

        #endregion

        #region if (i != null) manager.ConvertToUpperEnum(i); else manager.ConvertToUpper(value);

        var ifThenElse = Expression.IfThenElse(Expression.NotEqual(enumerable, Expression.Constant(null)),
         Expression.Call(visitorManager, _convertToUpperEnum, enumerable),
         Expression.Call(visitorManager, _convertToUpper, value));

        #endregion

        statements.Add(Expression.Block(new[] { value, enumerable }, assignValue, assignEnum, ifThenElse));
      }
    }

    // no blocks
    if (statements.Count <= 1)
      return null;

    return Expression.Lambda<Visitor>(Expression.Block(new[] { entityVar }, statements), visitorManager, entityParam).Compile();
  }
}

08-29 01:20