为啥这个 foreach 循环缺少类中的属性?
Posted
技术标签:
【中文标题】为啥这个 foreach 循环缺少类中的属性?【英文标题】:Why is this foreach loop missing a property from the class?为什么这个 foreach 循环缺少类中的属性? 【发布时间】:2021-06-23 13:51:47 【问题描述】:我基本上是在尝试使用反射将任何类展平为字典,以便我可以在 Blazor 中通用地使用和绑定它们。然后,我需要能够创建该类的一个实例并使用字典中的数据(将由组件更新)填充它。
例如
public class Order
public Guid Id get; set;
public Customer Customer get; set;
public string Address get; set;
public string Postcode get; set;
public List<string> Test get; set;
public List<Test> Test2 get; set;
public class Customer
public string FirstName get; set;
public string LastName get; set;
public string FullName => $"FirstName LastName";
public Gender Gender get; set;
public List<string> Test get; set;
应该变成:
"Id": "",
"Customer.FirstName": "",
"Customer.LastName": "",
"Customer.Gender": "",
"Customer.Test": "",
"Address": "",
"Postcode": "",
"Test": "",
"Test2": ""
由于某种原因,当我迭代 Order
类的属性时,Test2
被遗漏了。当我放置断点时,循环显示集合中的属性,它似乎只是跳过它。我以前从未见过这种情况。
代码:https://dotnetfiddle.net/g1qyVQ
我也不认为当前代码能够处理更深的嵌套深度,我希望它能够真正与任何 POCO 对象一起使用。
另外,如果有人知道一种更好的方法来做我正在尝试的事情,我很想找到一种更简单的方法。谢谢
【问题讨论】:
代码需要在帖子中。它也可以与人们尝试联系起来,但问题必须独立存在。请edit将代码放入帖子中。也就是说,它有很多代码。您需要将其分解为minimal reproducible example。 往返数据会很困难。因为您正在展平整个对象层次结构,所以您必须考虑树中任何地方的冲突。您需要使用 POCO 对象然后将它们展平成字典的根本原因是什么? 查看我的最新答案,了解我可以对什么/为什么给出的最佳解释 【参考方案1】:首先,在链接代码示例方面做得很好。如果没有这个,我会在大约三秒钟内通过这个问题。 :D
在GetAllProperties()
中,您的整个循环位于一个巨大的try catch
块内,catch 将返回当前字典,而不检查异常是什么。因此,如果您没有得到您期望的一切,您可能遇到了错误。
修改catch块:
catch (Exception ex) Console.WriteLine(ex.ToString()); return result;
现在,你可以看到问题了:
System.ArgumentException: An item with the same key has already been added. Key: Test
您的对象有多个名为“Test”的属性,但字典中的键必须是唯一的。
总结:错误不是敌人,而是你最好的朋友。不要使用try / catch
绕过错误。如果你这样做了,你可能会变得“神秘,以前从未见过这种情况!”结果。
【讨论】:
谢谢我完全错过了,我现在有一个更简单的版本,现在使用一个名为 JsonFlatten 的包来展平对象。我还决定首先创建对象的默认完全初始化版本,以便稍后我可以分配值。【参考方案2】:对于任何感兴趣的人,这里是我现在的位置:
https://dotnetfiddle.net/3ORKNs
using JsonFlatten;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json;
namespace RecursiveClassProperties
public static class Program
static void Main(string[] args)
var item = CreateDefaultItem(typeof(Order));
Console.WriteLine(JsonSerializer.Serialize(item, new JsonSerializerOptions WriteIndented = true ));
var json = JsonSerializer.Serialize(item);
var properties = JObject.Parse(json).Flatten();
Console.WriteLine(JsonSerializer.Serialize(properties, new JsonSerializerOptions WriteIndented = true ));
var formProperties = properties.ToDictionary(x => x.Key, x => new FormResponse(string.Empty));
Console.WriteLine(JsonSerializer.Serialize(formProperties, new JsonSerializerOptions WriteIndented = true ));
private static object CreateFormItem(Type type, Dictionary<string, FormResponse> formProperties, object result = null)
result = CreateDefaultItem(type);
return result;
private static object CreateDefaultItem(Type type, object result = null, object nested = null, bool isBase = false)
void SetProperty(PropertyInfo property, object instance)
if (property.PropertyType == typeof(string)) property.SetValue(instance, string.Empty);
if (property.PropertyType.IsEnum) property.SetValue(instance, 0);
if (property.PropertyType == typeof(Guid)) property.SetValue(instance, Guid.Empty);
if (result is null)
result = Activator.CreateInstance(type);
isBase = true;
var properties = type.GetProperties();
foreach (var property in properties)
if (!Attribute.IsDefined(property, typeof(FormIgnoreAttribute)) && property.GetSetMethod() is not null)
if (property.PropertyType == typeof(string) || property.PropertyType.IsEnum || property.PropertyType == typeof(Guid))
if (isBase) SetProperty(property, result);
else if (nested is not null && nested.GetType() is not IList && !nested.GetType().IsGenericType) SetProperty(property, nested);
else
var _nested = default(object);
if (isBase)
property.SetValue(result, Activator.CreateInstance(property.PropertyType));
_nested = property.GetValue(result);
if (nested is not null)
property.SetValue(nested, Activator.CreateInstance(property.PropertyType));
_nested = property.GetValue(nested);
CreateDefaultItem(property.PropertyType, result, _nested);
return result;
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public class FormIgnoreAttribute : Attribute
public class FormResponse
public FormResponse(string value) => Value = value;
public string Value get; set;
public class Order
public Guid Id get; set;
public Customer Customer get; set;
public string Address get; set;
public string Postcode get; set;
public Test Test get; set;
public List<Gender> Genders get; set;
public List<string> Tests get; set;
public enum Gender
Male,
Female
public class Test
public string Value get; set;
public List<Gender> Genders get; set;
public List<string> Tests get; set;
public class Customer
public string FirstName get; set;
public string LastName get; set;
public string FullName => $"FirstName LastName";
public Gender Gender get; set;
public Test Test get; set;
public List<Gender> Genders get; set;
public List<string> Tests get; set;
我的想法是我可以为 formProperties 赋值,将它传递给 CreateFormItem() 并返回一个填充的对象。我这样做的原因是因为我有一个 Blazor 组件 Table
,它有一个 typeparam TItem
,对于那些不熟悉 Blazor 的人来说,基本上可以将其视为 Table<TItem>
。然后为表格提供一个可以渲染的对象列表。
以这种方式展平对象既可以让我轻松地在表中显示类的所有属性和子属性,但最重要的是绑定“新项目”表单的输入,该表单会将新对象返回给外部的委托组件(回到正常的 .NET 中)提交到创建控制器(将其放入数据库中)。拥有 Dictionary
接下来我需要让 CreateFormItem() 返回带有来自表单的实际数据的对象。抱歉,如果这有点冗长,想不出更简洁的方式来解释它。
谢谢:)
【讨论】:
以上是关于为啥这个 foreach 循环缺少类中的属性?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我不能访问这个数组 ForEach 循环中的数据? SwiftUI