C# cast Dictionary to Dictionary (Involving Reflection)

前端 未结 8 978
无人及你
无人及你 2021-01-04 10:04

Is it possible to cast a Dictionary to a consistent intermediate generic type? So I would be able to cast <

相关标签:
8条回答
  • 2021-01-04 10:13

    Even if you could find some way to express this, it would be the wrong thing to do - it's not true that a Dictionary<string, bool> is a Dictionary<string, object>, so we definitely don't want to cast. Consider that if we could cast, we could try and put a string in as a value, which obviously doesn't fit!

    What we can do, however, is cast to the non-generic IDictionary (which all Dictionary<,>s implement), then use that to construct a new Dictionary<string, object> with the same values:

    FieldInfo field = this.GetType().GetField(fieldName);
    IDictionary dictionary = (IDictionary)field.GetValue(this);
    Dictionary<string, object> newDictionary = 
        dictionary
        .Cast<dynamic>()
        .ToDictionary(entry => (string)entry.Key,
                      entry => entry.Value);
    

    (note that you can't use .Cast<DictionaryEntry> here for the reasons discussed here. If you're pre-C# 4, and so don't have dynamic, you'll have to do the enumeration manually, as Gibsnag's answer does)

    0 讨论(0)
  • 2021-01-04 10:17

    Following AakashM's answer, the Cast doesn't seem to play ball. You can get around it by using a little helper method though:

    IDictionary dictionary = (IDictionary)field.GetValue(this);
    Dictionary<string, object> newDictionary = CastDict(dictionary)
                                               .ToDictionary(entry => (string)entry.Key,
                                                             entry => entry.Value);
    
    private IEnumerable<DictionaryEntry> CastDict(IDictionary dictionary)
    {
        foreach (DictionaryEntry entry in dictionary)
        {
            yield return entry;
        }
    }
    

    The duck typing in foreach is handy in this instance.

    0 讨论(0)
  • 2021-01-04 10:17

    Yes It is possible to cast the FieldInfo to Dictionary as below

    This is one example, I have used in my code

    Dictionary<string, string> 
                    GetTheDict = FilesAndPaths.GetType()
                                 .GetFields()
                                 .Where(f => f.Name.Equals(pLoadFile))
                                 .Select(f => (Dictionary<string, string>)f.GetValue(FilesAndPaths))
                                 .Single();
    
    0 讨论(0)
  • 2021-01-04 10:20

    When I stumbled onto the same situation, I created the following helper:

    /// <summary>
    /// Casts a dictionary object to the desired Dictionary type.
    /// </summary>
    /// <typeparam name="TKey">The target Key type.</typeparam>
    /// <typeparam name="TValue">The target value type.</typeparam>
    /// <param name="dictionary">The dictionary to cast.</param>
    /// <returns>A copy of the input dictionary, casted to the provided types.</returns>
    private Dictionary<TKey, TValue> CastDictionary<TKey, TValue>(IDictionary dictionary)
    {
        // Get the dictionary's type.
        var dictionaryType = typeof(Dictionary<TKey, TValue>);
    
        // If the input is not a dictionary.
        if (dictionaryType.IsAssignableFrom(typeof(Dictionary<,>)))
        {
            // Throw an exception.
            throw new Exception("The cast to a dictionary failed: The input object is not a dictionary.");
        }
    
        // Get the generic arguments of the dictionary.
        var arguments = dictionaryType.GetGenericArguments();
    
        // If the first type of the dictionary is not a descendant of TKey.
        if (!(arguments[0] is TKey || arguments[0].IsAssignableFrom(typeof(TKey)))
            // Or its second type is not a descendant of TValue.
            || !(arguments[1] is TValue || arguments[1].IsAssignableFrom(typeof(TValue))))
        {
            // Throw an exception.
            throw new Exception("The cast to a dictionary failed: The input dictionary's signature does not match <" + typeof(TKey).Name + ", " + typeof(TValue).Name + ">");
        }
    
        // Get the dictionary's default constructor.
        var constructor = dictionaryType.GetConstructor(Type.EmptyTypes);
    
        // Create a new dictionary.
        var output = (Dictionary<TKey, TValue>)constructor.Invoke(null);
    
        // Loop through the dictionary's entries.
        foreach (DictionaryEntry entry in dictionary)
        {
            // Insert the entries.
            output.Add((TKey)entry.Key, (TValue)entry.Value);
        }
    
        // Return the result.
        return output;
    }
    

    Could be used in your case as follows:

    FieldInfo field = (IDictionary)this.GetType().GetField(fieldName);
    
    Dictionary<string, Object> dict = CastDictionary<string, Object>(field.GetValue(this));
    
    0 讨论(0)
  • 2021-01-04 10:22

    Consider whether casting to and from object really is necessary. I started down that path and stumbled across this article, before realising I could achieve what I needed through generics rather than conversion. For example;

    class DictionaryUtils<T>
    {
        public static T ValueOrDefault(IDictionary<string, T> dictionary, string key)
        {
            return dictionary.ContainsKey(key) ? dictionary[key] : default(T);
        }
    }
    

    This bit of code is much cleaner and will be faster than the equivalent conversion code shown in other answers.

    0 讨论(0)
  • 2021-01-04 10:31

    Is this helping you ?

    Dictionary<a, b> output =
       input.ToDictionary(item => item.Key, item => (SomeType)item.Value);
    
    0 讨论(0)
提交回复
热议问题