是否有通用方法通过C#中的反射填充各种泛型集合?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了是否有通用方法通过C#中的反射填充各种泛型集合?相关的知识,希望对你有一定的参考价值。

例如,这里是通过反射创建List<int>的一些代码(是的,我知道有从数组转换到列表的函数,问题不是关于该解决方案,问题是关于使用反射而不事先知道类型。

public static T Convert<T>(int[] src)
{
    Type genericClassType = typeof(T);
    Type[] typeParameters = genericClassType.GetGenericArguments();
    Type genericTypeDef = genericClassType.GetGenericTypeDefinition();
    Type constructedClass = genericTypeDef.MakeGenericType(typeParameters);
    T arrayLike = (T)Activator.CreateInstance(constructedClass);

    System.Reflection.MethodInfo method = arrayLike.GetType().GetMethod("Add", typeParameters);

    foreach (int value in src) {
        method.Invoke(arrayLike, new []{(object)value});
    }

    return arrayLike;
}

所以我可以这样称呼它

int[] src = {4, 5, 6};
List<int> copy = Convert<List<int>>(src);

但我也希望能够这样称呼它

int[] src = {4, 5, 6};
Stack<int> copy = Convert<Stack<int>>(src);

int[] src = {4, 5, 6};
Queue<int> copy = Convert<Queue<int>>(src);

但我不能,因为例如Stack没有Add方法

实际上,我正在进行一些反序列化工作,并试图使其成为通用或半通用的。源数据没有类型信息。它只是一个整数的数组,但在调用反序列化代码时,我知道我希望它是什么类型ListStackQueue等...那么,是否有可能通常转换为给定类型或者我必须去写每种通用的自定义代码?

我肯定在某种程度上ListStackQueue是不同的但是C / C ++ /汇编程序员在我看到给定一个int数组和所需的类型所有信息都在那里重建给定只有一个通用的构造函数(已经存在于上面的代码)。

答案

对于您的特定情况,如果您只是更改Convert方法以将src作为参数传递给构造函数,它将起作用:

public static T Convert<T>(int[] src)
{
    Type genericClassType = typeof(T);
    Type[] typeParameters = genericClassType.GetGenericArguments();
    Type genericTypeDef = genericClassType.GetGenericTypeDefinition();
    Type constructedClass = genericTypeDef.MakeGenericType(typeParameters);

    T arrayLike = (T)Activator.CreateInstance(constructedClass, src);

    return arrayLike;
}

所以你可以像这样使用它:

int[] src = { 4, 5, 6 };
Stack<int> copy = Convert<Stack<int>>(src);
Queue<int> copy2 = Convert<Queue<int>>(src);
List<int> copy3 = Convert<List<int>>(src);

因此,您可能希望在构造函数中添加一些类型检查,以验证它是否可以接受IEnumerable<int>的类型。

但是你必须要知道顺序。例如,集合将按顺序包含:

copy

6, 5, 4

copy2copy3

4, 5, 6

如果你想让copy以相同的顺序包含它们,只需反转数组:

Stack<int> copy = Convert<Stack<int>>(src.Reverse().ToArray());


Try it out

另一答案

那么,是否有可能通常转换为给定的类型,或者我是否必须为每种类型的泛型编写自定义代码?

如果它们都有一个带有IEnumerable的构造函数,那就给它一个镜头:

public static T Convert<T>(int[] src) where T : new()
{
    var obj = Activator.CreateInstance(typeof(T), src);
    return (T)obj;
}

Here it is as a Fiddle.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        int[] src = {4, 5, 6};
        List<int> copy = Convert<List<int>>(src);
        Stack<int> copy1 = Convert<Stack<int>>(src);
        Queue<int> copy2 = Convert<Queue<int>>(src);

        Console.WriteLine(string.Join(",",copy));
        Console.WriteLine(string.Join(",",copy1));
        Console.WriteLine(string.Join(",",copy2));    
    }

    public static T Convert<T>(int[] src) where T : new()
    {
        var obj = Activator.CreateInstance(typeof(T), src);
        return (T)obj;
    }
}
另一答案

实际上,我正在进行一些反序列化工作,并试图使其成为通用或半通用的。源数据没有类型信息。它只是一个int的数组,但是当调用反序列化代码时,我知道我想要它的类型是List,Stack,Queue等...那么,是否可以通常转换为给定类型或者我必须继续写每种通用的自定义代码?

所以我理解的是每种类型可能有不同的方法向集合添加元素(并且可能会或可能不会将IEnumerable<T>作为构造函数参数),并且您需要在运行时指定它。我们可以使用Action

public static T Convert<T, TArray>(IEnumerable<TArray> src, Action<T, TArray> addMethod) where T : new()
{
    var myType = (T)Activator.CreateInstance(typeof(T));

    foreach (var element in src)
        addMethod(myType, element);

    return myType;
}

所以你需要指定add方法,你可以像这样调用代码:

var copy = Convert<Stack<int>, int>(src, (lst, i) => lst.Push(i));

在这种情况下,从堆栈弹出的第一个元素将是6。这是Stack<T>如何运作的本质。堆栈是LIFO(后进/先出)。因此,元素按照给定的顺序被压入堆栈(4,5,6),然后以相反的顺序(6,5,4)弹出。 Queue将以FIFO顺序呈现它们。

这适用于奇数集合类型:

public class SomeStrangeCollectionType<T>
{
    private List<T> _myList = new List<T>();

    public void Gibberish(T value)
    {
        _myList.Add(value); 
    }

    public T[] ToArray()
    {
        return _myList.ToArray();   
    }
}

你可以致电Convert

var strange = Convert<SomeStrangeCollectionType<int>, int>(src, (c, i) => c.Gibberish(i));

并将返回一个结果:

Console.WriteLine(string.Join(",", strange.ToArray()));
4,5,6

Try it out

以上是关于是否有通用方法通过C#中的反射填充各种泛型集合?的主要内容,如果未能解决你的问题,请参考以下文章

java 27 - 7 反射之 通过反射越过泛型检查

java 反射 , 判断Class是否是某个类的子类或父类

Class的实例是不可变的吗?

C#方法输入输出集合中的多个泛型类型

通过反射了解集合泛型的本质

Java反射的理解-- 通过反射了解集合泛型的本质