如何在 C# 中访问匿名类型的属性?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1203522/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 10:49:23  来源:igfitidea点击:

How to access property of anonymous type in C#?

c#.netobjectpropertiesanonymous-types

提问by wgpubs

I have this:

我有这个:

List<object> nodes = new List<object>(); 

nodes.Add(
new {
    Checked     = false,
    depth       = 1,
    id          = "div_" + d.Id
});

... and I'm wondering if I can then grab the "Checked" property of the anonymous object. I'm not sure if this is even possible. Tried doing this:

...我想知道我是否可以获取匿名对象的“Checked”属性。我不确定这是否可能。尝试这样做:

if (nodes.Any(n => n["Checked"] == false))... but it doesn't work.

if (nodes.Any(n => n["Checked"] == false))......但它不起作用。

Thanks

谢谢

采纳答案by Greg Beech

If you want a strongly typed list of anonymous types, you'll need to make the list an anonymous type too. The easiest way to do this is to project a sequence such as an array into a list, e.g.

如果您想要匿名类型的强类型列表,您还需要使列表成为匿名类型。最简单的方法是将序列(例如数组)投影到列表中,例如

var nodes = (new[] { new { Checked = false, /* etc */ } }).ToList();

Then you'll be able to access it like:

然后你就可以像这样访问它:

nodes.Any(n => n.Checked);

Because of the way the compiler works, the following then should also work once you have created the list, because the anonymous types have the same structure so they are also the same type. I don't have a compiler to hand to verify this though.

由于编译器的工作方式,一旦您创建了列表,以下内容也应该起作用,因为匿名类型具有相同的结构,因此它们也是相同的类型。我没有编译器来验证这一点。

nodes.Add(new { Checked = false, /* etc */ });

回答by Daniel Earwicker

If you're storing the object as type object, you need to use reflection. This is true of any object type, anonymous or otherwise. On an object o, you can get its type:

如果您将对象存储为 type object,则需要使用反射。这适用于任何对象类型,匿名或其他类型。在对象 o 上,您可以获取其类型:

Type t = o.GetType();

Then from that you look up a property:

然后从中查找属性:

PropertyInfo p = t.GetProperty("Foo");

Then from that you can get a value:

然后从中你可以得到一个值:

object v = p.GetValue(o, null);

This answer is long overdue for an update for C# 4:

这个答案早就应该更新 C# 4 了:

dynamic d = o;
object v = d.Foo;

And now another alternative in C# 6:

现在 C# 6 中的另一种选择:

object v = o?.GetType().GetProperty("Foo")?.GetValue(o, null);

Note that by using ?.we cause the resulting vto be nullin three different situations!

注意,通过使用?.我们导致生成的vnull在三种不同的情况!

  1. ois null, so there is no object at all
  2. ois non-nullbut doesn't have a property Foo
  3. ohas a property Foobut its real value happens to be null.
  1. onull,所以根本没有对象
  2. onull非但没有财产Foo
  3. o有一个财产,Foo但它的实际价值恰好是null.

So this is not equivalent to the earlier examples, but may make sense if you want to treat all three cases the same.

所以这不等同于前面的例子,但如果你想对所有三种情况一样对待这可能是有意义的。

回答by glennkentwell

You could iterate over the anonymous type's properties using Reflection; see if there is a "Checked" property and if there is then get its value.

您可以使用反射遍历匿名类型的属性;查看是否有“已检查”属性,如果有则获取其值。

See this blog post: http://blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx

请参阅此博客文章:http: //blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx

So something like:

所以像:

foreach(object o in nodes)
{
    Type t = o.GetType();

    PropertyInfo[] pi = t.GetProperties(); 

    foreach (PropertyInfo p in pi)
    {
        if (p.Name=="Checked" && !(bool)p.GetValue(o))
            Console.WriteLine("awesome!");
    }
}

回答by Matt

The accepted answer correctly describes how the list should be declared and is highly recommended for most scenarios.

接受的答案正确地描述了应该如何声明列表,并且强烈建议在大多数情况下使用。

But I came across a different scenario, which also covers the question asked. What if you have to use an existing object list, like ViewData["htmlAttributes"]in MVC? How can you access its properties (they are usually created via new { @style="width: 100px", ... })?

但是我遇到了一个不同的场景,它也涵盖了所提出的问题。如果您必须使用现有的对象列表,例如ViewData["htmlAttributes"]MVC 中怎么办?你如何访问它的属性(它们通常是通过创建的new { @style="width: 100px", ... })?

For this slightly different scenario I want to share with you what I found out. In the solutions below, I am assuming the following declaration for nodes:

对于这种略有不同的情况,我想与您分享我的发现。在下面的解决方案中,我假设以下声明nodes

List<object> nodes = new List<object>();

nodes.Add(
new
{
    Checked = false,
    depth = 1,
    id = "div_1" 
});

1. Solution with dynamic

1. 动态解决方案

In C# 4.0 and higherversions, you can simply cast to dynamic and write:

C# 4.0 及更高版本中,您可以简单地强制转换为动态并编写:

if (nodes.Any(n => ((dynamic)n).Checked == false))
    Console.WriteLine("found not checked element!");

Note:This is using late binding,which means it will recognize only at runtime if the object doesn't have a Checkedproperty and throws a RuntimeBinderExceptionin this case - so if you try to use a non-existing Checked2property you would get the following message at runtime:"'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'".

注:这是使用后期绑定,这意味着它只能识别在运行时,如果该对象不具有Checked财产和抛出一个RuntimeBinderException在这种情况下-因此,如果您尝试使用一个不存在的Checked2属性,您会收到以下消息在运行时间:"'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"

2. Solution with reflection

2. 用反射解决

The solution with reflection works both with old and new C# compilerversions. For old C# versions please regard the hint at the end of this answer.

带有反射的解决方案适用于旧的和新的 C# 编译器版本。对于旧的 C# 版本,请注意本答案末尾的提示。

Background

背景

As a starting point, I found a good answer here. The idea is to convert the anonymous data type into a dictionary by using reflection. The dictionary makes it easy to access the properties, since their names are stored as keys (you can access them like myDict["myProperty"]).

作为起点,我在这里找到了一个很好的答案。这个想法是通过使用反射将匿名数据类型转换为字典。字典使访问属性变得容易,因为它们的名称存储为键(您可以像 那样访问它们myDict["myProperty"])。

Inspired by the code in the link above, I created an extension class providing GetProp, UnanonymizePropertiesand UnanonymizeListItemsas extension methods, which simplify access to anonymous properties. With this class you can simply do the query as follows:

受上面链接中代码的启发,我创建了一个扩展类,提供GetProp,UnanonymizePropertiesUnanonymizeListItems作为扩展方法,它简化了对匿名属性的访问。使用这个类,您可以简单地执行如下查询:

if (nodes.UnanonymizeListItems().Any(n => (bool)n["Checked"] == false))
{
    Console.WriteLine("found not checked element!");
}

or you can use the expression nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()as ifcondition, which filters implicitly and then checks if there are any elements returned.

或者您可以使用表达式nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()作为if条件,它隐式过滤然后检查是否有任何元素返回。

To get the first object containing "Checked" property and return its property "depth", you can use:

要获取包含“Checked”属性的第一个对象并返回其属性“depth”,您可以使用:

var depth = nodes.UnanonymizeListItems()
             ?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");

or shorter: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];

或更短: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];

Note:If you have a list of objects which don't necessarily contain all properties (for example, some do not contain the "Checked" property), and you still want to build up a query based on "Checked" values, you can do this:

注意:如果您有一个不一定包含所有属性的对象列表(例如,有些对象不包含“已检查”属性),并且您仍希望基于“已检查”值构建查询,则可以做这个:

if (nodes.UnanonymizeListItems(x => { var y = ((bool?)x.GetProp("Checked", true)); 
                                      return y.HasValue && y.Value == false;}).Any())
{
    Console.WriteLine("found not checked element!");
}

This prevents, that a KeyNotFoundExceptionoccurs if the "Checked" property does not exist.

这可以防止KeyNotFoundException在“Checked”属性不存在时发生。



The class below contains the following extension methods:

下面的类包含以下扩展方法:

  • UnanonymizeProperties: Is used to de-anonymizethe properties contained in an object. This method uses reflection. It converts the object into a dictionary containing the properties and its values.
  • UnanonymizeListItems: Is used to convert a list of objects into a list of dictionaries containing the properties. It may optionally contain a lambda expression to filterbeforehand.
  • GetProp: Is used to return a single value matching the given property name. Allows to treat not-existing properties as null values (true) rather than as KeyNotFoundException (false)
  • UnanonymizeProperties: 用于对对象中包含的属性进行去匿名化。该方法使用反射。它将对象转换为包含属性及其值的字典。
  • UnanonymizeListItems: 用于将对象列表转换为包含属性的字典列表。它可以选择包含一个lambda 表达式来预先过滤
  • GetProp: 用于返回与给定属性名称匹配的单个值。允许将不存在的属性视为空值 (true) 而不是 KeyNotFoundException (false)

For the examples above, all that is required is that you add the extension class below:

对于上面的示例,您只需要在下面添加扩展类:

public static class AnonymousTypeExtensions
{
    // makes properties of object accessible 
    public static IDictionary UnanonymizeProperties(this object obj)
    {
        Type type = obj?.GetType();
        var properties = type?.GetProperties()
               ?.Select(n => n.Name)
               ?.ToDictionary(k => k, k => type.GetProperty(k).GetValue(obj, null));
        return properties;
    }

    // converts object list into list of properties that meet the filterCriteria
    public static List<IDictionary> UnanonymizeListItems(this List<object> objectList, 
                    Func<IDictionary<string, object>, bool> filterCriteria=default)
    {
        var accessibleList = new List<IDictionary>();
        foreach (object obj in objectList)
        {
            var props = obj.UnanonymizeProperties();
            if (filterCriteria == default
               || filterCriteria((IDictionary<string, object>)props) == true)
            { accessibleList.Add(props); }
        }
        return accessibleList;
    }

    // returns specific property, i.e. obj.GetProp(propertyName)
    // requires prior usage of AccessListItems and selection of one element, because
    // object needs to be a IDictionary<string, object>
    public static object GetProp(this object obj, string propertyName, 
                                 bool treatNotFoundAsNull = false)
    {
        try 
        {
            return ((System.Collections.Generic.IDictionary<string, object>)obj)
                   ?[propertyName];
        }
        catch (KeyNotFoundException)
        {
            if (treatNotFoundAsNull) return default(object); else throw;
        }
    }
}

Hint:The code above is using the null-conditionaloperators, available since C# version 6.0 - if you're working with older C# compilers (e.g. C# 3.0),simply replace ?.by .and ?[by [everywhere, e.g.

提示:上面的代码使用空条件运算符,自 C# 6.0 版起可用 - 如果您使用较旧的 C# 编译器(例如 C# 3.0),只需在任何地方替换?.by.?[by [,例如

var depth = nodes.UnanonymizeListItems()
            .FirstOrDefault(n => n.Contains("Checked"))["depth"];

If you're notforced to use an older C# compiler, keep it as is, because using null-conditionals makes null handling much easier.

如果您没有被迫使用较旧的 C# 编译器,请保持原样,因为使用 null 条件会使 null 处理变得更加容易。

Note:Like the other solution with dynamic, this solution is also using late binding, but in this case you're not getting an exception - it will simply not find the element if you're referring to a non-existing property, as long as you keep the null-conditionaloperators.

注意:与动态的其他解决方案一样,此解决方案也使用后期绑定,但在这种情况下,您不会收到异常 - 如果您指的是不存在的属性,它只会找不到元素,只要当您保留空条件运算符时。

What might be useful for some applications is that the property is referred to via a string in solution 2, hence it can be parameterized.

对于某些应用程序可能有用的是,通过解决方案 2 中的字符串引用该属性,因此可以对其进行参数化。

回答by orfruit

Recently, I had the same problem within .NET 3.5 (no dynamic available). Here is how I solved:

最近,我在 .NET 3.5 中遇到了同样的问题(没有动态可用)。这是我解决的方法:

// pass anonymous object as argument
var args = new { Title = "Find", Type = typeof(FindCondition) };

using (frmFind f = new frmFind(args)) 
{
...
...
}

Adapted from somewhere on stackoverflow:

改编自 stackoverflow 上的某处:

// Use a custom cast extension
public static T CastTo<T>(this Object x, T targetType)
{
   return (T)x;
}

Now get back the object via cast:

现在通过强制转换取回对象:

public partial class frmFind: Form
{
    public frmFind(object arguments)
    {

        InitializeComponent();

        var args = arguments.CastTo(new { Title = "", Type = typeof(Nullable) });

        this.Text = args.Title;

        ...
    }
    ...
}