让我们看一下LINQ-to-Objects。这段代码引入了多少低效率?
public object GetObjectToSerialize(object value, Type targetType)
{
var type = value.GetType();
PropertyInfo[] setToNullProperties;
if (!_typeToPropertyMap.TryGetValue(type, out setToNullProperties))
{
var allPropeties = type.GetProperties();
var passwordProperties = allPropeties
.Where(p => p.PropertyType == typeof(string))
.Where(p => p.Name.Contains("Password")).ToArray();
var passwordWithoutEncryptedAttribute = passwordProperties
.Where(p => !p.GetCustomAttributes(typeof(EncryptedConfigurationItemAttribute), false).Any());
if (passwordWithoutEncryptedAttribute.Any())
{
throw new InvalidOperationException();
}
var propertiesWithEncryptedAttribute = allPropeties.Where(p => p.GetCustomAttributes(typeof(EncryptedConfigurationItemAttribute), false).Any());
setToNullProperties = passwordProperties.Union(propertiesWithEncryptedAttribute).ToArray();
_typeToPropertyMap[type] = setToNullProperties;
}
foreach (var property in setToNullProperties)
{
property.SetValue(value, null, null);
}
return value;
}
我想知道很多.Where
.toArray
.Union
。
例如,ToArray
方法不知道输出的大小,因此必须进行多次分配。如果我们有int
s而不是PropertyInfo
的数组,它将从4个元素开始,然后根据需要保持加倍和复制元素。我们最终可能会有过多的存储空间。例如,如果我们最终得到33,000
个元素,我们就会浪费128KB
动态存储空间(32,000 X 4-byte ints
)。
当我发现passwordProperties
时,有两个委托对象分配,两个用于调用Enumerable.Where
。这些委托指向可能有两个不同的闭包对象,每个闭包对象都捕获了封闭变量。这些闭包对象是新类的实例,它们在二进制文件和运行时都占用了非常重要的空间。 (当然,参数现在存储在两个地方,必须复制到闭包对象,然后每次访问它们时都必须产生额外的间接。)很可能,Where
运算符将会分配新的IEnumerable
个对象。
如何通过使用声明式方式改进代码(因为随着编译器和运行时享受新的优化,它会变得更快)?
答案 0 :(得分:4)
if中的代码对每种类型执行一次。我没有看到任何问题。即使它比必要的慢2倍,它仍然足够快,并且执行得很少(免责声明:我写了那段代码:-))
但是你问过如何加速代码......你几乎可以删除所有LINQ:
public object GetObjectToSerialize(object value, Type targetType)
{
var type = value.GetType();
PropertyInfo[] setToNullProperties;
if (!_typeToPropertyMap.TryGetValue(type, out setToNullProperties))
{
PropertyInfo[] allProperties = type.GetProperties();
var setToNullProperties2 = new List<PropertyInfo>(allProperties);
foreach (PropertyInfo property in allProperties)
{
bool isEncrypted = property.GetCustomAttributes(typeof(EncryptedConfigurationItemAttribute), false).Any();
bool isPasswordProperty = false;
if (!isEncrypted)
{
isPasswordProperty = property.PropertyType == typeof(string) && property.Name.Contains("Password");
if (isPasswordProperty) {
throw new InvalidOperationException();
}
}
if (isEncrypted || isPasswordProperty) {
setToNullProperties2.Add(property);
}
}
_typeToPropertyMap[type] = setToNullProperties = setToNullProperties2.ToArray();
}
foreach (var property in setToNullProperties)
{
property.SetValue(value, null, null);
}
return value;
}
真正的加速是在Expression
树生成器中转换代码,因此您将删除重复反射(SetValue
部分)。问题是生成Expression
树并编译它非常慢,所以除非你在同一GetObjectToSerialize
上使用Type
一百次,否则你将没有加速。