如何在不知道键的类型和值的类型

时间:2017-12-29 03:14:28

标签: c#

说我有这样的方法:

public void Foo(object arguments)

并说我需要检测arguments的类型是否实际上是枚举。我会写这样的东西:

if (arguments is IEnumerable)

现在,假设我需要检测它是否是KeyValuePair的枚举(无论键的类型和值的类型如何)。我的直觉是写这样的东西:

if (arguments is IEnumerable<KeyValuePair<,>>)

但是visual studio抱怨Using the generic type 'KeyValuePair<TKey, TValue>' requires 2 type arguments

我也尝试过:

if (arguments is IEnumerable<KeyValuePair<object, object>>)

但如果键不是一个对象(例如string),或者该值不是一个对象(例如int),则返回false。 / p>

是否有人建议如何确定枚举是否包含KeyValuePairs,无论密钥类型和值类型如何,如果是,我如何循环这些对?

3 个答案:

答案 0 :(得分:1)

您可以将反射与GetGenericTypeDefinition

一起使用
var pair = new KeyValuePair<string, int>("asd", 123);

var isOf = pair.GetType().GetGenericTypeDefinition() == typeof(KeyValuePair<,>);

注意:如果它不是通用类型,它将抛出异常

InvalidOperationException
  

当前类型不是通用类型。也就是说,IsGenericType返回   假的。

您可以使用Type.IsGenericType

进行检查

我不确定你真的想要在这里实现,

foreach

中的一些荒谬的例子
private bool Get<T>(IEnumerable<T> list, Type someType) 
{
   foreach (var item in list)
   {
      if (item.GetType()
               .GetGenericTypeDefinition() == someType)
      {
         return true;
      }
   }
   return false;
}

private List<T> Get2<T>(IEnumerable<T> list, Type someType)
{
   return list.Where(
         item => item.GetType()
                     .GetGenericTypeDefinition() == someType)
      .ToList();
}}

<强>用法

var blah = Get2(list, typeof(KeyValuePair<,>));
  

注意:这一切真的取决于你想做什么,很难   告诉我,以上这些都只是不知道的愚蠢的例子   事先键入,可能无法满足您的需求。

其他信息

Type.GetGenericTypeDefinition Method ()

Type.IsGenericType Property

答案 1 :(得分:1)

你需要反思一下:

Boolean isKeyValuePair = false;

Type type = arguments.GetType();

if (type.IsGenericType)
{
    Type[] genericTypes = type.GetGenericArguments();

    if (genericTypes.Length == 1)
    {
        Type underlyingType = genericTypes[0];

        if (underlyingType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
            isKeyValuePair = true;
    }
}

为了重建Enumerable并对其进行迭代,您可以使用以下使用dynamic的方法:

List<KeyValuePair<Object, Object>> list = new List<KeyValuePair<Object, Object>>();

foreach (dynamic kvp in (IEnumerable)arguments)
    list.Add(new KeyValuePair<Object, Object>(kvp.Key, kvp.Value));

或,LINQ

List<KeyValuePair<Object, Object>> list = (from dynamic kvp in (IEnumerable)arguments select new KeyValuePair<Object, Object>(kvp.Key, kvp.Value)).ToList();

我也找到了另一个解决方案,但这是纯粹的疯狂:

Boolean isKeyValuePair = false;

Type type = arguments.GetType();

if (type.IsGenericType)
{
    Type[] genericTypes = type.GetGenericArguments();

    if (genericTypes.Length == 1)
    {
        Type underlyingType = genericTypes[0];

        if (underlyingType.GetGenericTypeDefinition() == typeof (KeyValuePair<,>))
        {
            Type[] kvpTypes = underlyingType.GetGenericArguments();

            Type kvpType = typeof(KeyValuePair<,>);
            kvpType = kvpType.MakeGenericType(kvpTypes);

            Type listType = typeof (List<>);
            listType = listType.MakeGenericType(kvpType);

            dynamic list = Activator.CreateInstance(listType);

            foreach (dynamic argument in (IEnumerable)arguments)
                list.Add(Activator.CreateInstance(kvpType, argument.Key, argument.Value));
        }
    }
}

参考文献:

答案 2 :(得分:0)

感谢@ tommaso-belluzzo和@sarumanfor投入时间帮我解决这个问题。我最终接受了Tommaso的回答,因为我非常喜欢他使用dynamic类型来枚举枚举中的项目的想法。

我也想提供我的解决方案(主要基于Tommasso的建议)

/// <summary>Get the key=>value pairs represented by a dictionary, enumeration of KeyValue pairs or an anonymous object.</summary>
private IEnumerable<KeyValuePair<string, object>> GetArguments(object arguments)
{
    // null
    if (arguments == null)
        return Enumerable.Empty<KeyValuePair<string, object>>();

    // dictionary
    if (arguments is IDictionary dictionary)
        return dictionary
            .Cast<dynamic>()
            .Select(item => new KeyValuePair<string, object>(item.Key.ToString(), item.Value));

    // enumeration
    if (arguments is IEnumerable enumeration)
    {
#if NETFULL
        var argumentsType = enumeration.GetType();
        var itemType = argumentsType.GetElementType();
        var isGeneric = itemType.IsGenericType;
        var enumeratedType = isGeneric ? itemType.GetGenericTypeDefinition() : null;
#else
        var argumentsTypeInfo = enumeration.GetType().GetTypeInfo();
        var itemTypeInfo = argumentsTypeInfo.GetElementType().GetTypeInfo();
        var isGeneric = itemTypeInfo.IsGenericType;
        var enumeratedType = isGeneric ? itemTypeInfo.GetGenericTypeDefinition() : null;
#endif

        if (enumeratedType == typeof(KeyValuePair<,>))
        {
            return enumeration
                .Cast<dynamic>()
                .Select(item => new KeyValuePair<string, object>(item.Key.ToString(), item.Value));
        }
        else
        {
            throw new ArgumentException("You must provide an enumeration of KeyValuePair", nameof(arguments));
        }
    }

    // object
    return arguments.GetType()
        .GetRuntimeProperties()
        .Where(p => p.CanRead)
        .Select(p => new KeyValuePair<string, object>(p.Name, p.GetValue(arguments)));
}