简体   繁体   English

如何在不知道键的类型和值的类型的情况下迭代KeyValuePair的枚举

[英]How to iterate through an enumeration of KeyValuePair without knowing the type of the key and the type of the value

Say I have a method like this: 说我有这样的方法:

public void Foo(object arguments)

and say I need to detect if the type of arguments is actually an enumeration. 并说我需要检测arguments的类型是否实际上是一个枚举。 I would write something like this: 我会写这样的东西:

if (arguments is IEnumerable)

Now, let's say I need to detect if it's an enumeration of KeyValuePair (regardless of the type of the key and the type of the value). 现在,假设我需要检测它是否是KeyValuePair的枚举(无论键的类型和值的类型如何)。 My instinct would be to write something like this: 我的直觉是写这样的东西:

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

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

I also tried: 我也尝试过:

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

but it returns false if the key is anything but an object (such as a string for example) or if the value is anything but an object (such as an int for example). 但如果键不是一个对象(例如一个string ),或者如果该值只是一个对象(例如int ),则返回false。

Does anybody have suggestions how I could determine if an enumeration contains KeyValuePairs regardless of the key type and the value type and if so, how can I loop through these pairs? 有没有人建议如何确定枚举是否包含KeyValuePairs,无论密钥类型和值类型如何,如果是这样,我如何循环这些对?

You can use reflection with GetGenericTypeDefinition 您可以将反射与GetGenericTypeDefinition

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

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

Note : It will throw an exception if it isn't a generic type 注意:如果它不是泛型类型,它将抛出异常

InvalidOperationException

The current type is not a generic type. 当前类型不是通用类型。 That is, IsGenericType returns false. 也就是说,IsGenericType返回false。

You can check this with Type.IsGenericType 您可以使用Type.IsGenericType进行检查

I'm not sure really want you want to achieve here however, 我不确定你真的想要在这里实现,

Some nonsensical examples in a foreach 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();
}}

Usage 用法

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

Note : All this really depends on what you want to do, its hard to tell, both of these above are only silly examples of not knowing the type beforehand, and may not work for what you want. 注意 :所有这些都取决于你想要做什么,很难说,以上这些都只是事先不知道类型的愚蠢的例子,并且可能不适合你想要的。

Additional information 附加信息

Type.GetGenericTypeDefinition Method () Type.GetGenericTypeDefinition方法()

Type.IsGenericType Property Type.IsGenericType属性

You need some reflection here: 你需要在这里反思一下:

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;
    }
}

In order to rebuild an Enumerable and iterate over it, you could use the following approach that uses dynamic : 为了重建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));

or, with LINQ : 或者,使用LINQ

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

I also found another solution, but this is pure madness: 我也找到了另一个解决方案,但这是纯粹的疯狂:

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));
        }
    }
}

References: 参考文献:

Thank you @tommaso-belluzzo and @sarumanfor for investing the time to help me figure this out. 感谢@ tommaso-belluzzo和@sarumanfor投入时间帮我解决这个问题。 I ended up accepting Tommaso's answer because I really like his idea of using the dynamic type to enumerate items in the enumeration. 我最终接受了Tommaso的回答,因为我非常喜欢使用dynamic类型来枚举枚举中的项目。

I also wanted to contribute my solution (which is largely based on Tommasso's suggestions) 我也想贡献我的解决方案(主要基于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)));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM