首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将[name,value]字符串值映射到类,而不反射

将[name,value]字符串值映射到类,而不反射
EN

Stack Overflow用户
提问于 2021-05-14 20:46:35
回答 2查看 57关注 0票数 0

在使用反射将字符串属性名和字符串属性值映射到类时,我遇到了一个巨大的性能问题。

我现在的问题是:

代码语言:javascript
复制
  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; }
}

我使用反射将键值对集合映射到类

代码语言:javascript
复制
[{"Property1": "some value"}, {"Property2": "something else"},{"Property3","Property4","value" }.....{"Property100","val"}]

我现在使用反射映射了大约10,000个类实例,性能可以说是差劲的。

任何消除反射的想法都将受到极大的赞赏。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-05-15 00:04:34

我看到了两个选项,如果你需要避免像这样的任务的反射(当代码可以编程生成时)。

首先是

表达式

我经常用它,例如我看到一些人写这样的东西

代码语言:javascript
复制
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";

而且代码会很复杂,不管怎么说,它看起来像这样

代码语言:javascript
复制
   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();
        }
    }

这只是一个例子,如果有非字符串属性,它将失败。

它可以像这样使用

代码语言:javascript
复制
    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重启后消失了,但仍然看起来不是很有用。

因此,这是一个生成器,它为每个具有属性的类创建一个转换器。它又是一个样本,它不会检查很多东西。

代码语言:javascript
复制
    [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)
    {
    }
}

代码语言:javascript
复制
    static void Main(string[] args)
    {
        var t1 = GeneratedConverters.TestConverter.Convert(new Dictionary<string, string>
        {
            { "S3", "Value3" },
            { "S2", "Value2" },
        });
    }
票数 1
EN

Stack Overflow用户

发布于 2021-05-14 22:02:14

没有反射的最佳性能是手动映射。您的键/值对集合似乎是常规的JSON。因此,您可以使用JSON.NET中的JSONTextReader并读取字符串。然后手动将JSON属性映射到类属性。

如下所示:

代码语言:javascript
复制
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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67534590

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档