在使用反射将字符串属性名和字符串属性值映射到类时,我遇到了一个巨大的性能问题。
我现在的问题是:
public class Person
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public string Property4 { get; set; }
// My class has around 100 properties
public string Property100 { get; set; }
}我使用反射将键值对集合映射到类
[{"Property1": "some value"}, {"Property2": "something else"},{"Property3","Property4","value" }.....{"Property100","val"}]我现在使用反射映射了大约10,000个类实例,性能可以说是差劲的。
任何消除反射的想法都将受到极大的赞赏。
发布于 2021-05-15 00:04:34
我看到了两个选项,如果你需要避免像这样的任务的反射(当代码可以编程生成时)。
首先是
表达式
我经常用它,例如我看到一些人写这样的东西
public class A
{
public Prop1 ...
....
public Prop100
public override ToString() => $"{nameof(Prop1)}={Prop1};...";因此,对于所有100个属性,总是手动执行此操作。
有了Expression,它可以很容易地自动化,你只需要为String.Concat生成表达式,并将属性和名称的列表传递到那里。
对于您的示例,不清楚您的数据是什么。如何在列表中查找?让我们假设有一个dictionary(您可以将元组列表转换为字典),并且所有属性也都是字符串。
然后我们需要生成一个列表赋值表达式,如下所示: if(data.ContainsKey("Prop1")) result.Prop1 = data"Prop1";
而且代码会很复杂,不管怎么说,它看起来像这样
private static class CompiledDelegate<T>
{
public static Action<T, Dictionary<string, string>> initObject;
static CompiledDelegate()
{
var i = Expression.Parameter(typeof(Dictionary<string, string>), "i");
var v = Expression.Parameter(typeof(T), "v");
var propertyInfos = typeof(T).GetProperties().ToArray();
var t = new Dictionary<string, string>();
var contains = typeof(Dictionary<string, string>).GetMethod(nameof(Dictionary<string, string>.ContainsKey));
var getter = typeof(Dictionary<string, string>).GetProperties().First(x => x.GetIndexParameters().Length > 0);
var result = new List<Expression>();
foreach (var propertyInfo in propertyInfos)
{
var cst = Expression.Constant(propertyInfo.Name);
var assignExpression =
Expression.IfThen(Expression.Call(i, contains, cst),
Expression.Assign(Expression.PropertyOrField(v, propertyInfo.Name), Expression.MakeIndex(i, getter, new[] { cst })));
result.Add(assignExpression);
}
var block = Expression.Block(result);
initObject = Expression.Lambda<Action<T, Dictionary<string, string>>>(block, new ParameterExpression[] { v, i }).Compile();
}
}这只是一个例子,如果有非字符串属性,它将失败。
它可以像这样使用
static void Main(string[] args)
{
var tst = new Test();
CompiledDelegate<Test>.initObject(tst, new Dictionary<string, string>
{
{ "S3", "Value3" },
{ "S2", "Value2" },
});
CompiledDelegate<Test>.initObject(tst, new Dictionary<string, string>
{
{ "S3", "Value3" },
{ "S1", "Value1" },
});
Console.ReadKey();
}第二种选择实际上是理想的实现方式。
使用源代码生成器
我认为这样的事情必须在构建时间内完成。
在msdn上有很多文章,例如示例。但事实证明,它的实现并不是很容易,甚至只是一个示例。
我可以说,它对我不起作用,而我试着根据样本来做。为了让它正常工作,我不得不将TargetFramework改为netstandard2.0,做一些其他的事情……
但毕竟,当build是绿色的时候,Visual Studio仍然显示错误。
好吧,它在VS重启后消失了,但仍然看起来不是很有用。
因此,这是一个生成器,它为每个具有属性的类创建一个转换器。它又是一个样本,它不会检查很多东西。
[Generator]
public class ConverterGenerator : ISourceGenerator
{
private static string mytemplate = @"using System.Collections.Generic;
using {2};
namespace GeneratedConverters
{{
public static class {0}Converter
{{
public static {0} Convert(Dictionary<string, string> data)
{{
var result = new {0}();
{1}
return result;
}}
}}
}}";
public static string GetNamespaceFrom(SyntaxNode s)
{
if (s.Parent is NamespaceDeclarationSyntax namespaceDeclarationSyntax)
{
return namespaceDeclarationSyntax.Name.ToString();
}
if (s.Parent == null)
return "";
return GetNamespaceFrom(s.Parent);
}
public void Execute(GeneratorExecutionContext context)
{
GetMenuComponents(context, context.Compilation);
}
private static void GetMenuComponents(GeneratorExecutionContext context, Compilation compilation)
{
var allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes());
var allClasses = allNodes.Where(d => d.IsKind(SyntaxKind.ClassDeclaration)).OfType<ClassDeclarationSyntax>();
var classes = allClasses
.Where(c => c.AttributeLists.SelectMany(a => a.Attributes).Select(a => a.Name).Any(s => s.ToString().Contains("DictionaryConverter")))
.ToImmutableArray();
foreach (var item in classes.Distinct().Take(1))
{
context.AddSource(item.Identifier.Text + "Converter", String.Format(mytemplate, item.Identifier.Text, SourceText.From(GenerateProperties(item)), GetNamespaceFrom(item)));
}
}
private static string GenerateProperties(ClassDeclarationSyntax s)
{
var properties = s.Members.OfType<PropertyDeclarationSyntax>();
return String.Join(Environment.NewLine,
properties.Select(p =>
{
var name = p.Identifier.Text;
return $"if(data.ContainsKey(\"{name}\")) result.{name} = data[\"{name}\"];";
}));
}
public void Initialize(GeneratorInitializationContext context)
{
}
}和
static void Main(string[] args)
{
var t1 = GeneratedConverters.TestConverter.Convert(new Dictionary<string, string>
{
{ "S3", "Value3" },
{ "S2", "Value2" },
});
}发布于 2021-05-14 22:02:14
没有反射的最佳性能是手动映射。您的键/值对集合似乎是常规的JSON。因此,您可以使用JSON.NET中的JSONTextReader并读取字符串。然后手动将JSON属性映射到类属性。
如下所示:
JsonTextReader reader = new JsonTextReader(new StringReader(jsonString));
while (reader.Read())
{
if (reader.Value != null)
{
// check reader.Value.ToString() and assign to correct class property
}
}欲了解更多信息,请访问JSON.NET网站:https://www.newtonsoft.com/json/help/html/ReadingWritingJSON.htm
https://stackoverflow.com/questions/67534590
复制相似问题