Хотя ваш вопрос касается производительности кода, существует другая проблема, которую другие, похоже, упускают: ремонтопригодность.
Хотя вы можете подумать, что это не так важно, как проблема с производительностью, наличие кода, более удобочитаемого и обслуживаемого, облегчит решение проблем.
Вот пример того, как ваш код может выглядеть после нескольких рефакторингов:
class HierarchyUpperCaseConverter
{
private HashSet<object> visited = new HashSet<object>();
public static void ConvertToUpper(object entity)
{
new HierarchyUpperCaseConverter_v1().ProcessEntity(entity);
}
private void ProcessEntity(object entity)
{
// Don't process null references.
if (entity == null)
{
return;
}
// Prevent processing types that already have been processed.
if (this.visited.Contains(entity))
{
return;
}
this.visited.Add(entity);
this.ProcessEntity(entity);
}
private void ProcessEntity(object entity)
{
var properties =
this.GetProcessableProperties(entity.GetType());
foreach (var property in properties)
{
this.ProcessEntityProperty(entity, property);
}
}
private IEnumerable<PropertyInfo> GetProcessableProperties(Type type)
{
var properties =
from property in type.GetProperties()
where property.CanRead && property.CanWrite
where !property.PropertyType.IsValueType
where !(property.Name.Contains("password") &&
property.PropertyType == typeof(string))
select property;
return properties;
}
private void ProcessEntityProperty(object entity, PropertyInfo property)
{
object value = property.GetValue(entity, null);
if (value != null)
{
if (value is IEnumerable)
{
this.ProcessCollectionProperty(value as IEnumerable);
}
else if (value is string)
{
this.ProcessStringProperty(entity, property, (string)value);
}
else
{
this.AlterHierarchyToUpper(value);
}
}
}
private void ProcessCollectionProperty(IEnumerable value)
{
foreach (object item in (IEnumerable)value)
{
// Make a recursive call.
this.AlterHierarchyToUpper(item);
}
}
private void ProcessStringProperty(object entity, PropertyInfo property, string value)
{
string upperCaseValue = ConvertToUpperCase(value);
property.SetValue(entity, upperCaseValue, null);
}
private string ConvertToUpperCase(string value)
{
// TODO: ToUpper is culture sensitive.
// Shouldn't we use ToUpperInvariant?
return value.ToUpper();
}
}
Хотя этот код более чем в два раза длиннее вашего фрагмента кода, он более удобен в обслуживании. В процессе рефакторинга вашего кода я даже обнаружил возможную ошибку в вашем коде. Эту ошибку намного сложнее обнаружить в вашем коде. В своем коде вы пытаетесь преобразовать все строковые значения в верхний регистр, но не конвертируете строковые значения, которые хранятся в свойствах объекта. Посмотрите, например, на следующий код.
class A
{
public object Value { get; set; }
}
var a = new A() { Value = "Hello" };
Возможно, это именно то, что вы хотели, но строка "Hello" не преобразуется в "HELLO" в вашем коде.
Еще одна вещь, которую я хотел бы отметить, это то, что хотя я пытался сделать ваш код более читабельным, мой рефакторинг кажется примерно на 20% быстрее.
После рефакторинга кода я попытался улучшить его производительность, но обнаружил, что его особенно сложно улучшить. В то время как другие пытаются распараллелить код, я должен предупредить об этом. Распараллелить код не так просто, как могут подумать другие. Между потоками происходит некоторая синхронизация (в форме «посещенной» коллекции). Не забывайте, что запись в коллекцию не является поточно-ориентированной. Использование поточно-ориентированной версии или ее блокировка может снова снизить производительность. Вам придется проверить это.
Я также обнаружил, что узким местом реальной производительности является все отражение (особенно чтение всех значений свойств). Единственный способ действительно ускорить это - жестко кодировать операции кода для каждого типа или, как другие предлагали облегченную генерацию кода. Однако это довольно сложно, и сомнительно, стоит ли оно того.
Я надеюсь, что вы найдете мои рефакторинги полезными и желаю вам удачи в улучшении производительности.