如何在 C# 中迭代匿名对象的属性?
我想将匿名对象作为方法的参数,然后迭代其属性以将每个属性/值添加到动态 ExpandoObject
中。
所以我需要的是了解
new { Prop1 = "first value", Prop2 = SomeObjectInstance, Prop3 = 1234 }
每个属性的名称和值,并能够将它们添加到 ExpandoObject
中。
我该如何实现这个目标?
旁注:这将在我的许多单元测试中完成(我使用它来重构设置中的大量垃圾),因此性能在某种程度上是相关的。我对反射了解不够,无法肯定地说,但据我了解,它对性能的影响很大,所以如果可能的话,我宁愿避免它......
后续问题: 正如我所说,我将这个匿名对象作为方法的参数。我应该在方法的签名中使用什么数据类型?如果我使用object
,所有属性都可用吗?
I want to take an anonymous object as argument to a method, and then iterate over its properties to add each property/value to a a dynamic ExpandoObject
.
So what I need is to go from
new { Prop1 = "first value", Prop2 = SomeObjectInstance, Prop3 = 1234 }
to knowing names and values of each property, and being able to add them to the ExpandoObject
.
How do I accomplish this?
Side note: This will be done in many of my unit tests (I'm using it to refactor away a lot of junk in the setup), so performance is to some extent relevant. I don't know enough about reflection to say for sure, but from what I've understood it's pretty performance heavy, so if it's possible I'd rather avoid it...
Follow-up question:
As I said, I'm taking this anonymous object as an argument to a method. What datatype should I use in the method's signature? Will all properties be available if I use object
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
在 ASP.NET 中,您可以使用
RouteValueDictionary
类快速将匿名变量转换为属性字典。在内部它也使用反射,但它还维护属性的内部缓存。因此后续调用会快得多(证明)
因此,如果 (!) 您正在编写 ASP.NET 应用程序,那么您可以使用
P.S.每次编写 ASP.NET 代码时都会使用此类:
In ASP.NET you can use the
RouteValueDictionary
class to quickly convert an anonymous variable into a properties dictionary.Internally it uses reflection too, but it also maintains an internal cache of properties. So subsequent calls will be much faster (proof)
So if (!) you are writing an ASP.NET app, then you can use
P.S. This class is used every time you write ASP.NET code like this:
反思匿名对象以获取其属性名称和值,然后利用实际上是字典的 ExpandoObject 来填充它。这是一个以单元测试表示的示例:
Reflect on the anonymous object to get its property names and values, then take advantage of an ExpandoObject actually being a dictionary to populate it. Here's an example, expressed as a unit test:
另一种方法是使用
DynamicObject
而不是ExpandoObject
,这样,如果您实际尝试从其他对象访问属性,则只有进行反射的开销。现在,它仅在您实际尝试通过动态获取访问属性时才进行反射。缺点是,如果您重复访问同一属性,则每次都必须进行反射。因此,您可以缓存结果:
您可以支持存储目标对象列表以合并其属性,并支持设置属性(使用名为 TrySetMember) 允许您动态设置缓存字典中的值。
当然,反射的开销可能不值得担心,但对于大型对象,这可能会限制它的影响。也许更有趣的是它为您提供的额外灵活性。
An alternative approach is to use
DynamicObject
instead ofExpandoObject
, and that way you only have the overhead of doing the reflection if you actually try to access a property from the other object.Now it only does the reflection when you actually try to access the property via a dynamic get. On the downside, if you repeatedly access the same property, it has to do the reflection each time. So you could cache the result:
You could support storing a list of target objects to coalesce their properties, and support setting properties (with a similar override called TrySetMember) to allow you to dynamically set values in the cache dictionary.
Of course, the overhead of reflection is probably not going to be worth worrying about, but for large objects this could limit the impact of it. What is maybe more interesting is the extra flexibility it gives you.
这是一个老问题,但现在您应该能够使用以下代码来执行此操作:
输出如下所示:
This is an old question, but now you should be able to do this with the following code:
The output would look like the following:
你必须使用反射......(从此网址“借用”的代码)
you have to use reflection.... (code "borrowed" from this url)
使用 Reflection.Emit 创建一个通用方法来填充 ExpandoObject。
或者也许使用表达式(我认为这只能在 .NET 4 中实现)。
这些方法在调用时都不使用反射,仅在委托设置期间(显然需要缓存)。
下面是一些 Reflection.Emit 代码来填充字典(我猜 ExpandoObject 已经不远了);
Use Reflection.Emit to create a generic method to fill an ExpandoObject.
OR use Expressions perhaps (I think this would only be possible in .NET 4 though).
Neither of these approaches uses reflection when invoking, only during setup of a delegate (which obviously needs to be cached).
Here is some Reflection.Emit code to fill a dictionary (I guess ExpandoObject is not far off);