【问题标题】:.NET reflection: determining whether an array of T would be convertible to some other type.NET 反射:确定 T 数组是否可以转换为其他类型
【发布时间】:2009-12-11 07:44:23
【问题描述】:

注意,这个问题有点微妙,所以请仔细阅读:我不只是想找出某些 artibitrary 类型是否实现了 IEnumerable:

这是我用初始实现编写的函数:

    // is "toType" some sort of sequence that would be satisfied
    // by an array of type T? If so, what is the type of T?
    // e.g.
    // getArrayType(typeof(string[]) == typeof(string)
    // getArrayType(typeof(IEnumerable<int>)) == typeof(int)
    // getArrayType(typeof(List<string>)) == null // NOTE - Array<string> does not convert to List<string>
    // etc.
    private static Type getArrayType(Type toType)
    {
        if (toType.IsArray)
        {
            if (toType.GetArrayRank() != 1)
                return null;
            return toType.GetElementType();
        }
        // Look for IEnumerable<T>, and if so, return the type of T
        if (toType.IsGenericType && toType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            return toType.GetGenericArguments()[0];

        return null;
    }

能不能做得更好,处理更多的案件?例如目前

getType(typeof(ICollection<string>)) == null 

但 string[] 可以转换为 ICollection

还要注意,我事先并不知道“元素”类型是什么。

上下文是:我正在编写与脚本语言的反射绑定,如果您将 object[] 传递给期望 IEnumerable 的某个方法,我希望它“正常工作”(它将转换输入数组到一个字符串,在这种情况下)。

为了澄清,假设我有一些方法签名:

void WriteCSV(ICollection<string> fields);

我的脚本解释器有一个对象数组,这些对象都恰好可以转换为字符串:

object[] fields = new object[] { "one", 2, "three" };

然后我的脚本解释器需要弄清楚在这种情况下真正需要的是一个字符串数组。

而我希望我的脚本解释器放弃,说:

void WriteCSV(IRecord record);

即使 IRecord 甚至可能实现一些 IEnumerable:

interface IRecord : IEnumerable<string>
{
    void OtherMethods();
}

我无法从任何数组中构造一个 IRecord。

所以仅仅找出一个类型实现的 IEnumerables 并不是我所需要的。我这很微妙不是吗?

【问题讨论】:

  • 您能说明一下您的要求是什么吗?你的意图是实现 IEnumerable 的任何东西都应该在这里返回非 null 吗?
  • 已澄清。我需要找出可以转换为给定类型的数组类型(如果有的话)。 IEnumerable 是,我认为,我有点红鲱鱼。
  • 仅供参考 - 目前您的逻辑仅处理正在传递的数组,因为您永远不会将接口 IEnumerable 传递给方法,只有实现该接口的类。

标签: .net reflection generics


【解决方案1】:

可以通过创建泛型参数的数组并查看它是否实现目标类型来完成。示例代码仅检查接口,因为array 确保仅实现接口(IList、ICollection、IEnumerable)。此外,我从示例中返回了数组的类型(只是为了清楚起见),在您的代码中,您将希望返回您发布的示例代码中的通用参数。

private static Type GetArrayType(Type toType)
{
    if (toType.IsArray)
    {
        if (toType.GetArrayRank() != 1)
            return null;
        return toType;
    }

    // Verifies the type is generic and has only one generic argument
    if (toType.IsGenericType && toType.GetGenericArguments().Length == 1)
    {
        // Creates an array of the generic argument, e.g. IList<int> -> int[]
        var arrayOfType = toType.GetGenericArguments()[0].MakeArrayType();

        // Checks if the toType is an interface that the array implements
        if (arrayOfType.GetInterfaces().Contains(toType))
        {
            return arrayOfType    // arrayOfType.GetGenericArguments()[0];
        }
    }    
    return null;
}

public static void Test()
{
    Assert.AreEqual(typeof(int[]), GetArrayType(typeof(IList<int>)));
    Assert.AreEqual(typeof(int[]), GetArrayType(typeof(ICollection<int>)));
    Assert.AreEqual(typeof(int[]), GetArrayType(typeof(IEnumerable<int>)));

    Assert.IsNull(GetArrayType(typeof(List<int>)));
    Assert.IsNull(GetArrayType(typeof(Dictionary<int, string>)));
}

希望对你有帮助。

【讨论】:

    【解决方案2】:

    根据我在您上一个问题中的回答:

    您可以使用这段代码获取特定类型上IEnumberable&lt;T&gt; 接口的所有实现,然后提取它们的泛型参数。

    Type type = typeof(ICollection<string>);
    
    IEnumerable<Type> elementTypes = type.GetInterfaces()
        .Where(i => i.IsGenericType 
            && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        .Select(i => i.GetGenericArguments()[0]);
    

    在本例中,elementTypes 将是包含 System.String 类型的单个元素。

    如何处理元素类型取决于您。由于一个接口可以实现多次,因此您不妨修改查询以获得单个实现。

    【讨论】:

    • 问题是逆变。我不想知道 IEnumerables 类型可以转换成什么,我想知道 T 的数组是否可以转换成 to 类型,如果可以,是什么T 需要是...例如,如果类型是 List,则实现 IEnumerable 但我 想要 返回 null,因为 string[] 不能转换为 List.
    • 嗯...有趣的要求...我再给它一个 bash。
    【解决方案3】:

    数组案例被IEnumerable&lt;T&gt; 覆盖为一维数组(您使用GetArrayrank 查找的数组)实现IList&lt;T&gt;,而IList&lt;T&gt; 又派生自IEnumerable&lt;T&gt;。 接下来寻找IEnumerable 并将元素类型视为对象是有意义的——这就是foreach 构造处理集合的方式。

    要考虑的另一件事是提供的类型多次实现IEnumerable&lt;T&gt; 的情况(例如,IEnumerable&lt;object&gt;IEnumerable&lt;string&gt;)。例如,DataContractSerializer 在这种情况下不会将该类型视为集合类型。

    【讨论】:

    • 见说明。我不只是想找到该类型实现的 IEnumerables。我想知道哪种数组类型(如果有)可以转换为相关类型。
    猜你喜欢
    • 2016-05-06
    • 2018-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-13
    相关资源
    最近更新 更多