C# 将IEnumerable转换为IEnumerable<;T>;当T在编译时未知时

C# 将IEnumerable转换为IEnumerable<;T>;当T在编译时未知时,c#,generics,C#,Generics,我有一个具有以下签名的排序扩展方法: public static IEnumerable<T> CustomSort<T>(this IEnumerable<T> source, string sortProperties) 公共静态IEnumerable CustomSort(此IEnumerable源,字符串sortProperties) 我们不久前写的,它一直在做它的事情。现在我正在创建一个自定义控件,DataSource属性是IEnumerable

我有一个具有以下签名的排序扩展方法:

public static IEnumerable<T> CustomSort<T>(this IEnumerable<T> source, string sortProperties)
公共静态IEnumerable CustomSort(此IEnumerable源,字符串sortProperties)
我们不久前写的,它一直在做它的事情。现在我正在创建一个自定义控件,DataSource属性是IEnumerable(非泛型)。 有没有办法获取非泛型IEnumerable中对象的类型


我确信“对自定义控件数据源进行排序”的问题已经解决了一百万次,但我似乎找不到解决方案。

就我个人而言,我只想使用

DataSource.Cast()
然后就有了一个
IEnumerable
,可以在
CustomSort
函数中使用。我在这里假设这个函数已经可以处理任意对象了;根据第二个参数名判断,我猜您在其中使用了反射,所以应该没问题。只要确保它在思考时使用每个对象的
GetType()
,而不是
typeof(T)
,因为
typeof(T)
显然是
object
,它不会得到实际对象的字段列表

当然,如果您在编译时确实知道数据源中所有对象的类型,您可能希望改用该类型,例如:

DataSource.Cast<Customer>()
DataSource.Cast()

这里有一个基本问题,即一个类型可以同时为多个T实现IEnumerable-of-T。但如果我们排除这一情况,一种厚颜无耻的做法是:

void Evil<T>(IEnumerable<T> data) {...}

IEnumerable source = ...
dynamic cheeky = source;
Evil(cheeky);
void Evil(IEnumerable数据){…}
IEnumerable源=。。。
动态厚颜无耻=来源;
邪恶(厚颜无耻);

这基本上将这个问题转移到DLR上,让您的邪恶的T方法轻松解决。

您可以创建一个扩展方法,在运行时返回正确的类型:

public static class LinqExtensions
{
    public static Type GetElementType(this IEnumerable source)
    {
        var enumerableType = source.GetType();
        if (enumerableType.IsArray)
        {
            return enumerableType.GetElementType();
        }
        if (enumerableType.IsGenericType)
        {
            return enumerableType.GetGenericArguments().First();
        }
        return null;
    }
}
更新:我添加了一种机制,用于对非泛型
IEnumerable
进行泛型特定的
IEnumerable
排序

public static class SortingExtensions
{
    public static IEnumerable<T> CustomSort<T>(this IEnumerable<T> source, string sortProperties)
    {
        // sort here
    }

    public static IEnumerable CustomSort(this IEnumerable source, string sortProperties)
    {
        var elementType = source.GetElementType();
        var genericElementType = typeof (IEnumerable<>).MakeGenericType(elementType);

        var sortMethod = typeof (SortingExtensions).GetMethod(
            "CustomSort", 
            BindingFlags.Public | BindingFlags.Static,
            null, 
            new [] {genericElementType, typeof (string)},
            null);

        return (IEnumerable) sortMethod.Invoke(null, new object[] {source, sortProperties});
    }

}
公共静态类排序扩展
{
公共静态IEnumerable CustomSort(此IEnumerable源,字符串sortProperties)
{
//在这里分类
}
公共静态IEnumerable CustomSort(此IEnumerable源,字符串sortProperties)
{
var elementType=source.GetElementType();
var genericElementType=typeof(IEnumerable);
var sortMethod=typeof(SortingExtensions).GetMethod(
“自定义排序”,
BindingFlags.Public | BindingFlags.Static,
无效的
新[]{genericElementType,typeof(string)},
无效);
return(IEnumerable)sortMethod.Invoke(null,新对象[]{source,sortProperties});
}
}

关于MoveNext()你说得对。在我写这篇文章的时候,我已经删除了那一行,所以我是凭记忆写的。关于演员阵容,我试试看。谢谢大家!@EladLachmi:好的,那么我将冒昧地从您的问题中删除这一部分。CustomSort函数在反射中使用typeof(T)将排序构造为lambda表达式。我宁愿利用我们所拥有的,而不是创造一些新的东西(这需要经过QA等,等等…你知道它是怎样的:)。这假设
源代码实际实现了
IEnumerable
对于一些
T
;NET中的许多数据源都源于泛型之前,因此实际上只实现了
IEnumerable
,在这种情况下,您的代码会因
RuntimeBinderException
@Timwi而崩溃。问题是如何“转换”它。只有当它确实为某些T实现了泛型版本时,才有可能强制转换它。我并不反对你的观点-我只是说这个依赖性在问题中是隐含的。@Timwi-Marc的假设是正确的,即非泛型IEnumerable(数据源)的起源是一些T的列表,这在编译时对我来说是未知的。因此它应该实现IEnumerable。在我的情况下,我将如何实现它?该方法是IEnumerable上的一种推广方法。。。我需要传递什么作为泛型参数?我理解您的解决方案的概念,但我缺乏实现它的机制:)@elad您不能将扩展语法用于动态-只需让您的扩展方法通过显式SomeType.邪恶(厚颜无耻)语法有趣的方法调用第二个方法。我也要试一试。关于排序方法,这或多或少是我们正在做的事情,但我们也考虑了多个属性的排序,每个属性都有自己的方向。我尝试了您的解决方案,但enumerableType.GetGenericArguments().First()会导致StackOverflowException。(很久没有了!:)我发现了问题。一旦该方法通过公共静态IEnumerable CustomSort(此IEnumerable源,字符串sortProperties),它将再次被调用,这将创建一个内循环,或者看起来是这样。在
Invoke
中,您将
source
作为第一个参数传递,它将再次调用非泛型方法。这会导致内循环。我将您的
CustomSort
重命名为
CustomSort2
,然后
sortMethod
为空。