如何动态调用通用扩展方法?

发布于 2024-08-22 20:25:05 字数 390 浏览 5 评论 0原文

我写了这个扩展方法:

public static DataTable ToDataTable<T>(this IList<T> list)
{...}

如果使用编译时已知的类型调用它效果很好:

DataTable tbl = new List<int>().ToDataTable();

但是如果泛型类型未知,如何调用它?

object list = new List<int>();
...
tbl = Extension.ToDataTable((List<object>)list); // won't work

I wrote this extension method:

public static DataTable ToDataTable<T>(this IList<T> list)
{...}

It works well if called with a type known at compile time:

DataTable tbl = new List<int>().ToDataTable();

But how to call it if the generic type isn't known?

object list = new List<int>();
...
tbl = Extension.ToDataTable((List<object>)list); // won't work

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

北笙凉宸 2024-08-29 20:25:05

发生这种情况是因为 List 不是 List—— List 类型在其元素类型参数中不是协变的。不幸的是,您需要获取通用方法的类型化版本并使用反射调用它:

Type listItemType = typeof(int);   // cheating for simplicity - see below for real approach
MethodInfo openMethod = typeof(Extension).GetMethod("ToDataTable", ...);
MethodInfo typedMethod = openMethod.MakeGenericMethod(typeof(listItemType));
typedMethod.Invoke(null, new object[] { list });

另一种方法可能是创建一个接受 IList 而不是 IList的扩展方法版本。List 类实现了这个非泛型接口以及泛型接口,因此您将能够调用:(

public static DataTable WeakToDataTable(this IList list) { ... }

((IList)list).WeakToDataTable();

实际上您可能会使用重载而不是不同的名称 - - 只是使用不同的名称来调用不同的类型。)


更多信息:在反射解决方案中,我跳过了如何确定列表元素类型的问题。这可能有点棘手,具体取决于您想要达到的复杂程度。如果您假设该对象将是 List (对于某些 T),那么很简单:

Type listItemType = list.GetType().GetGenericArguments()[0];

如果您只愿意假设 IList那么这就有点困难了,因为您需要找到适当的接口并从中获取通用参数。并且您不能使用 GetInterface(),因为您正在寻找通用接口的封闭构造实例。因此,您必须仔细检查所有接口,寻找 IList 的实例:

foreach (Type itf in list.GetType().GetInterfaces())
{
  if (itf.IsGenericType && itf.GetGenericTypeDefinition == typeof(IList<>))  // note generic type definition syntax
  {
    listItemType = itf.GetGenericArguments()[0];
  }
}

这适用于空列表,因为它会超出元数据,而不是列表内容。

This occurs because a List<int> is not a List<object> -- the List type is not covariant in its element type parameter. Unfortunately you would need to get a typed version of the generic method and call it using reflection:

Type listItemType = typeof(int);   // cheating for simplicity - see below for real approach
MethodInfo openMethod = typeof(Extension).GetMethod("ToDataTable", ...);
MethodInfo typedMethod = openMethod.MakeGenericMethod(typeof(listItemType));
typedMethod.Invoke(null, new object[] { list });

An alternative may be to create a version of your extension method that accepts IList rather than IList<T>. The List<T> class implements this non-generic interface as well as the generic interface, so you will be able to call:

public static DataTable WeakToDataTable(this IList list) { ... }

((IList)list).WeakToDataTable();

(In reality you'd probably use an overload rather than a different name -- just using a different name to call out the different types.)


More info: In the reflection solution, I skipped over the problem of how to determine the list element type. This can be a bit tricky depending on how sophisticated you want to get. If you're assuming that the object will be a List<T> (for some T) then it's easy:

Type listItemType = list.GetType().GetGenericArguments()[0];

If you're only willing to assume IList<T> then it's a bit harder, because you need to locate the appropriate interface and get the generic argument from that. And you can't use GetInterface() because you're looking for a closed constructed instance of a generic interface. So you have to grovel through all the interfaces looking for one which is an instance of IList<T>:

foreach (Type itf in list.GetType().GetInterfaces())
{
  if (itf.IsGenericType && itf.GetGenericTypeDefinition == typeof(IList<>))  // note generic type definition syntax
  {
    listItemType = itf.GetGenericArguments()[0];
  }
}

This will work for empty lists because it goes off the metadata, not the list content.

夕色琉璃 2024-08-29 20:25:05

在使用 IList 接口时遇到问题后,我使用 IList 接口解决了这个问题,例如 itowlson 提议。由于 _T 方法,它有点难看,但效果很好:

DataTable tbl = ((IList)value).ToDataTable();

public static class Extensions
{
    private static DataTable ToDataTable(Array array) {...}
    private static DataTable ToDataTable(ArrayList list) {...}
    private static DataTable ToDataTable_T(IList list) {...}

    public static DataTable ToDataTable(this IList list)
    {
        if (list.GetType().IsArray)
        {
            // handle arrays - int[], double[,] etc.
            return ToDataTable((Array)list);
        }
        else if (list.GetType().IsGenericType)
        {
            // handle generic lists - List<T> etc.
            return ToDataTable_T(list);
        }
        else
        {
            // handle non generic lists - ArrayList etc.
            return ToDataTable((ArrayList)list);
        }            
    }
}

After having trouble to get it working with the IList<T> interface I solved it using the IList interface like itowlson proposed. It's a little bit ugly because of the _T method but it works well:

DataTable tbl = ((IList)value).ToDataTable();

public static class Extensions
{
    private static DataTable ToDataTable(Array array) {...}
    private static DataTable ToDataTable(ArrayList list) {...}
    private static DataTable ToDataTable_T(IList list) {...}

    public static DataTable ToDataTable(this IList list)
    {
        if (list.GetType().IsArray)
        {
            // handle arrays - int[], double[,] etc.
            return ToDataTable((Array)list);
        }
        else if (list.GetType().IsGenericType)
        {
            // handle generic lists - List<T> etc.
            return ToDataTable_T(list);
        }
        else
        {
            // handle non generic lists - ArrayList etc.
            return ToDataTable((ArrayList)list);
        }            
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文