Answers:
如果要使用匿名类型的强类型列表,则还需要使该列表成为匿名类型。最简单的方法是将序列(例如数组)投影到列表中,例如
var nodes = (new[] { new { Checked = false, /* etc */ } }).ToList();
然后,您将可以像以下方式访问它:
nodes.Any(n => n.Checked);
由于编译器的工作方式,创建列表后,以下内容也应工作,因为匿名类型具有相同的结构,因此它们也具有相同的类型。我没有编译器可以验证这一点。
nodes.Add(new { Checked = false, /* etc */ });
如果将对象存储为type object
,则需要使用反射。对于任何对象类型(匿名或其他),都是如此。在对象o上,可以得到其类型:
Type t = o.GetType();
然后从中查找属性:
PropertyInfo p = t.GetProperty("Foo");
然后,您可以得到一个值:
object v = p.GetValue(o, null);
对于C#4的更新,此答案早就应该提交:
dynamic d = o;
object v = d.Foo;
现在,C#6中的另一个替代方法是:
object v = o?.GetType().GetProperty("Foo")?.GetValue(o, null);
注意,通过使用?.
我们导致生成的v
是null
在三种不同的情况!
o
是null
,所以根本没有物体o
不是,null
但没有财产Foo
o
具有属性,Foo
但其实际价值恰好是null
。因此,这并不等同于先前的示例,但是如果您想将所有三种情况都视为相同,则可能是有道理的。
object v = d.Foo
),则将获得运行时异常;如果该属性不存在,GetValue(o, null)
则将为null。
GetProperty
将返回null
,GetValue
如果通过则将抛出null
,因此总体效果是一个例外。C#4.0版本提供了更具描述性的例外。
您可以使用Reflection遍历匿名类型的属性。查看是否存在“已检查”属性,然后获取其值。
请参阅此博客文章:http : //blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx
所以像这样:
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!");
}
}
可接受的答案正确地描述了如何声明列表,并且强烈建议在大多数情况下使用。
但是我遇到了另一种情况,它也涵盖了所提出的问题。如果您必须使用现有的对象列表(例如ViewData["htmlAttributes"]
在MVC中)怎么办?您如何访问其属性(通常是通过创建的new { @style="width: 100px", ... }
)?
对于这种稍微不同的情况,我想与您分享我发现的内容。在下面的解决方案中,我假设以下声明nodes
:
List<object> nodes = new List<object>();
nodes.Add(
new
{
Checked = false,
depth = 1,
id = "div_1"
});
在C#4.0和更高版本中,您可以简单地转换为动态并编写:
if (nodes.Any(n => ((dynamic)n).Checked == false))
Console.WriteLine("found not checked element!");
注意:这使用的是后期绑定,这意味着只有在对象不具有Checked
属性的RuntimeBinderException
情况下,它才会在运行时识别,并在这种情况下抛出-因此,如果您尝试使用不存在的Checked2
属性,则会在以下位置收到以下消息:运行时: "'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
。
反射解决方案适用于新旧C#编译器版本。对于旧的C#版本,请考虑此答案末尾的提示。
背景
首先,我在这里找到了一个很好的答案。这个想法是通过使用反射将匿名数据类型转换为字典。字典使访问属性变得容易,因为它们的名称存储为键(您可以像一样访问它们myDict["myProperty"]
)。
通过在上面的链接代码的启发,我创建的扩展类,它提供GetProp
,UnanonymizeProperties
并UnanonymizeListItems
作为扩展方法,它简化了访问匿名属性。使用此类,您可以简单地执行查询,如下所示:
if (nodes.UnanonymizeListItems().Any(n => (bool)n["Checked"] == false))
{
Console.WriteLine("found not checked element!");
}
或者您可以使用表达式nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
作为if
条件,该条件会进行隐式过滤,然后检查是否返回了任何元素。
要获取第一个包含“ Checked”属性的对象并返回其“ depth”属性,可以使用:
var depth = nodes.UnanonymizeListItems()
?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");
或更短: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
注意:如果您有一个对象列表,这些对象不一定包含所有属性(例如,某些对象不包含“ Checked”属性),并且您仍想基于“ Checked”值建立查询,则可以做这个:
if (nodes.UnanonymizeListItems(x => { var y = ((bool?)x.GetProp("Checked", true));
return y.HasValue && y.Value == false;}).Any())
{
Console.WriteLine("found not checked element!");
}
KeyNotFoundException
如果“ Checked”属性不存在,这可以防止出现a 。
下面的类包含以下扩展方法:
UnanonymizeProperties
:用于取消匿名化对象中包含的属性。此方法使用反射。它将对象转换为包含属性及其值的字典。UnanonymizeListItems
:用于将对象列表转换为包含属性的字典列表。它可以选择包含一个lambda表达式以进行预先过滤。GetProp
:用于返回与给定属性名称匹配的单个值。允许将不存在的属性视为空值(true),而不是KeyNotFoundException(false)对于上面的示例,所需要做的就是在下面添加扩展类:
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;
}
}
}
提示:上面的代码使用空条件的运营商,因为C#6.0版可用-如果你正在使用的旧的C#编译器(例如C#3.0),只需更换?.
由.
并且?[
通过[
随处可见,如
var depth = nodes.UnanonymizeListItems()
.FirstOrDefault(n => n.Contains("Checked"))["depth"];
如果您不被迫使用较旧的C#编译器,则应保持其不变,因为使用null条件将使null处理变得更加容易。
注意:像其他具有动态解决方案的解决方案一样,该解决方案也使用了后期绑定,但是在这种情况下,您不会遇到异常-如果您引用的是不存在的属性,则只要找不到该元素,就不会找到该元素因为您保留了空条件运算符。
对于某些应用程序可能有用的是,该属性是通过解决方案2中的字符串引用的,因此可以对其进行参数化。
最近,我在.NET 3.5(没有动态可用)中遇到了相同的问题。这是我解决的方法:
// pass anonymous object as argument
var args = new { Title = "Find", Type = typeof(FindCondition) };
using (frmFind f = new frmFind(args))
{
...
...
}
从stackoverflow上的某个地方改编而成:
// Use a custom cast extension
public static T CastTo<T>(this Object x, T targetType)
{
return (T)x;
}
现在通过强制转换返回对象:
public partial class frmFind: Form
{
public frmFind(object arguments)
{
InitializeComponent();
var args = arguments.CastTo(new { Title = "", Type = typeof(Nullable) });
this.Text = args.Title;
...
}
...
}