C# 使用IEnumerable GetGenericArguments获取类型
我开发了一个用于生成显示和可编辑表的MVC助手(需要一个jquery插件来允许在可编辑表中动态添加和删除带有完整回发的行),例如 与属性一起使用的将包括页脚中的总计、为查看和编辑链接添加列、为复杂类型呈现超链接等C# 使用IEnumerable GetGenericArguments获取类型,c#,linq,modelmetadata,C#,Linq,Modelmetadata,我开发了一个用于生成显示和可编辑表的MVC助手(需要一个jquery插件来允许在可编辑表中动态添加和删除带有完整回发的行),例如 与属性一起使用的将包括页脚中的总计、为查看和编辑链接添加列、为复杂类型呈现超链接等 [TableColumn(IncludeTotals = true)] 我将在CodeProject上发布它,但在发布之前,我想解决一个问题。 助手首先从表达式中获取ModelMetadata,检查它是否实现了ICollection,然后获取集合中的类型(请注意,下面的代码片段来自接
[TableColumn(IncludeTotals = true)]
我将在CodeProject上发布它,但在发布之前,我想解决一个问题。
助手首先从表达式中获取ModelMetadata
,检查它是否实现了ICollection
,然后获取集合中的类型(请注意,下面的代码片段来自接受的答案,但如下所述,并不完全正确)
该类型用于为表头(表中可能没有任何行)和表体中的每一行生成ModelMetadata
(如果某些项是具有附加属性的继承类型,否则会破坏列布局)
我希望能够使用IEnumerable
而不是ICollection
,这样就不需要对linq表达式调用.ToList()
在大多数情况下,IEnumerable
工作正常,例如
IEnumerable items = MyCollection.Where(i => i....);
是正常的,因为.GetGenericArguments()
返回一个仅包含一种类型的数组。
问题是,某些查询上的“.GetGenericArguments()”返回2个或更多类型,并且似乎没有逻辑顺序。比如说
IEnumerable items = MyCollection.OrderBy(i => i...);
返回集合中的[0]类型,[1]用于排序的类型
在这种情况下,.GetGenericArguments()[0]
仍然有效,但是
MyCollection.Select(i => new AnotherItem()
{
ID = i.ID,
Name = 1.Name
}
返回[0]原始集合中的类型和[1]另一个项目的类型
因此,.GetGenericArguments()[1]
是我为另一个项目
呈现表格所需要的
我的问题是,是否有一种可靠的方法使用条件语句来获取呈现表所需的类型
从我目前的测试来看,使用.GetGenericArguments().Last()
在所有情况下都有效,除非使用OrderBy()
,因为排序键是最后一种类型
到目前为止,我尝试过的一些事情包括忽略值类型的类型(通常情况下,OrderBy()
),但是OrderBy()
查询可能使用字符串(可以检查),或者更糟的是,使用重载==、运算符的类(在这种情况下,我无法判断哪种类型是正确的),并且我无法找到一种方法来测试集合是否实现了iorderenumerable
已解决(使用发布的注释)
私有静态类型GetCollectionType(IEnumerable集合)
{
Type=collection.GetType();
if(type.IsGenericType)
{
Type[]types=Type.GetGenericArguments();
if(types.Length==1)
{
返回类型[0];
}
其他的
{
//如果实现两个IEnumerable,则可以为null
返回类型.GetInterfaces().Where(t=>t.IsGenericType)
.Where(t=>t.GetGenericTypeDefinition()==typeof(IEnumerable))
.SingleOrDefault().GetGenericArguments()[0];
}
}
else if(collection.GetType().IsArray)
{
返回类型:GetElementType();
}
//TODO:谁知道呢,但它可能不适合在表中渲染
返回null;
}
因此,如果我理解的话,您可以提供一些IEnumerable
,这是(通常)一些任意LINQ查询的结果。您希望可靠地获得IEnumerable
的T
部分?@Chris Yes,尽管它可能只是IEnumerable(例如ArrayList)如果是这样,请尝试迭代实现的接口,找到IEnumerable
接口并提取T
部分:var type=items.GetType().GetInterfaces().Where(T=>T.IsGenericType).Where(T=>T.GetGenericTypeDefinition()==typeof(IEnumerable)).Single().GetGenericArguments()[0]
请注意我使用的单个
:如果接口没有实现,它将失败,或者如果它实现了两个(或更多)不同的IEnumerable
接口,然后失败。您可以将其更改为使用FirstOrDefault
或First
,并根据需要处理这些额外的情况。对于您的注释,ArrayList
未键入。您可以提取第一项并调用其GetType()
方法。但是如果它没有项,那么你就不走运了:很难说。也不能保证你有一组混合的对象,或者想要使用基类而不是GetType()
返回的派生类。如果你愿意,如果它没有实现IEnumerable
(只是IEnumerable
)然后您可以将其视为单元测试中的类型对象
?@Chris,检查实现的接口看起来很有希望。关于您使用的SingleOrDefault
,以及您的注释“如果实现两个IEnumerable,则可能为null”;这不是真的。SingleOrDefault
将在存在多个异常时引发异常。即使它确实返回了null
(如果它没有实现任何IEnumerable
接口,它也会抛出异常),当您尝试调用GetGenericArguments
时,您将直接获得一个NullReferenceException
。我建议您分解查询并执行更精确的检查。此外,我个人更喜欢先执行接口检查,而不是默认为第一个泛型类型参数。虽然不太可能,但它是仍然可以有一个类定义,如class MyStuff:IEnumerable
;在这种情况下,最有可能的是,特别是如果您稍后对它们进行迭代(通过使用foreach
或调用GetEnumerator
),您会喜欢int
类型(因为这将是迭代的内容)而不是Foo
类型。
IEnumerable items = MyCollection.Where(i => i....);
IEnumerable items = MyCollection.OrderBy(i => i...);
MyCollection.Select(i => new AnotherItem()
{
ID = i.ID,
Name = 1.Name
}
private static Type GetCollectionType(IEnumerable collection)
{
Type type = collection.GetType();
if (type.IsGenericType)
{
Type[] types = type.GetGenericArguments();
if (types.Length == 1)
{
return types[0];
}
else
{
// Could be null if implements two IEnumerable
return type.GetInterfaces().Where(t => t.IsGenericType)
.Where(t => t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.SingleOrDefault().GetGenericArguments()[0];
}
}
else if (collection.GetType().IsArray)
{
return type.GetElementType();
}
// TODO: Who knows, but its probably not suitable to render in a table
return null;
}