C# 将 Dictionary 强制转换为 Dictionary

本文关键字:string Dictionary 转换 Obje AnyType | 更新日期: 2023-09-27 18:31:33

是否可以将Dictionary<string, Anything>转换为一致的中间泛型类型? 所以我可以将<string, string><string, bool><string, int><string, anything>全部投射到同一类型的字典中?

我正在从事一个使用大量反射的项目,我需要能够处理 DIctionary 类型,如下所示:

FieldInfo field = this.GetType().GetField(fieldName);
Dictionary<string, Object> dict = (Dictionary<string, Object>)field.GetValue(this);

上面的代码是我目前拥有的代码,程序总是在 cast from 字段失败。获取值到泛型Dictionary<string, Object> .

有没有办法做到这一点? 还是我应该找出一种不同的方法来处理这些词典?

任何帮助将不胜感激。

C# 将 Dictionary<string、AnyType> 强制转换为 Dictionary<string、Obje

按照AakashM的回答,演员似乎不会打球。不过,您可以使用一些辅助方法绕过它:

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

在这种情况下,鸭子输入foreach很方便。

这对

你有帮助吗?

Dictionary<a, b> output =
   input.ToDictionary(item => item.Key, item => (SomeType)item.Value);

即使你能找到某种方式来表达这一点,那也是错误的做法——Dictionary<string, bool>不是真的 Dictionary<string, object> ,所以我们绝对不想投。考虑一下,如果我们可以转换,我们可以尝试将string作为值放入,这显然不适合!

但是,我们可以做的是强制转换为非泛型IDictionary(所有Dictionary<,>都实现),然后使用它来构造具有相同值的新Dictionary<string, object>

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

(请注意,由于此处讨论的原因,您不能在此处使用.Cast<DictionaryEntry>。如果你是C#4之前,所以没有dynamic,你必须手动进行枚举,就像Gibsnag的答案一样)

当我偶然发现同样的情况时,我创建了以下帮助程序:

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

可以在您的情况下按如下方式使用:

FieldInfo field = (IDictionary)this.GetType().GetField(fieldName);
Dictionary<string, Object> dict = CastDictionary<string, Object>(field.GetValue(this));

解释什么是错误的:方差

如本回答所述:

Dictionary<string, bool>不是真的Dictionary<string,object>

让我解释一下原因。这一切都与 C# 中的协方差和逆变有关。总之,Dictionary<K, V>中的键类型K和值类型V既不是纯粹的输入参数,也不是整个字典中的输出参数。因此,这两种泛型类型都不能强制转换为较弱或较强的类型。

如果强制转换为较弱的类型,则会中断输入。例如,期望类型为 K, V 或更强的 Add 函数不能接受K, V中的任何一个的超类型。如果强制转换为更强的类型,则会中断输出。例如,索引器属性返回类型 V 。我们如何才能安全地将其转换为V的子类型,只知道原始类型是V?我们不能。

使用变体泛型的类型安全部分解决方案

有一种方法允许强类型转换,但仅限于原始字典接口的部分片段,这些片段在泛型参数的协方差/逆变方面是一致的。此解决方案使用实现一堆部分接口的包装器类型。每个分部接口都有键/值类型的协方差/逆方差的特定类型组合。然后我们将所有这些部分接口组合到一个主接口中,并通过一个支持对象(一个常规字典)来实现它。来了。首先,接口:

public interface IDictionaryBase
{
    int Count { get; }
    bool IsReadOnly { get; }
    void Clear();
}
public interface IInKeyInValueSemiDictionary<in K, in V> : IDictionaryBase, IInKeySemiDictionary<K>
{
    V this[K key] { set; }
    void Add(K key, V value);
}
public interface IInKeyOutValueSemiDictionary<in K, out V> : IDictionaryBase, IInKeySemiDictionary<K>, IOutValueSemiDictionary<V>
{
    V this[K key] { get; }
    ISuccessTuple<V> TryGetValue(K key);
}
public interface ISuccessTuple<out V>
{
    bool WasSuccessful { get; }
    V Value { get; }
}
public class SuccessTupleImpl<V> : ISuccessTuple<V>
{
    public bool WasSuccessful { get; }
    public V Value {get;}
    public SuccessTupleImpl(bool wasSuccessful, V value)
    {
        WasSuccessful = wasSuccessful;
        Value = value;
    }
}
public interface IInKeySemiDictionary<in K> : IDictionaryBase
{
    bool ContainsKey(K key);
    bool Remove(K key);
}
public interface IOutKeySemiDictionary<out K> : IDictionaryBase
{
    IEnumerable<K> Keys { get; }
}
public interface IOutValueSemiDictionary<out V> : IDictionaryBase
{
    IEnumerable<V> Values { get; }
}

注意:对于泛型参数,我们不必在这里涵盖所有 in/out 组合,并且还注意到某些接口只需要一个泛型参数。原因是协方差/逆变的某些组合没有任何关联的方法,因此不需要相应的类型。请注意,我省略了KeyValuePair<K, V>类型-如果您想包含它,则需要对此执行类似的技巧,而现阶段似乎不值得。

因此,接下来,所有这些接口的联合:

public interface IVariantDictionary<K, V> : IInKeyInValueSemiDictionary<K, V>, IInKeyOutValueSemiDictionary<K, V>,
    IOutKeySemiDictionary<K>, IDictionary<K, V>
{ }

然后,包装类:

class VariantDictionaryImpl<K, V> : IVariantDictionary<K, V>
{
    private readonly IDictionary<K, V> _backingDictionary;
    public VariantDictionaryImpl(IDictionary<K, V> backingDictionary)
    {
        _backingDictionary = backingDictionary ?? throw new ArgumentNullException(nameof(backingDictionary));
    }
    public V this[K key] { set => _backingDictionary[key] = value; }
    V IInKeyOutValueSemiDictionary<K, V>.this[K key] => _backingDictionary[key];
    V IDictionary<K, V>.this[K key] { get => _backingDictionary[key]; set => _backingDictionary[key] = value; }
    public int Count => _backingDictionary.Count;
    public bool IsReadOnly => _backingDictionary.IsReadOnly;
    public IEnumerable<K> Keys => _backingDictionary.Keys;
    public ICollection<V> Values => _backingDictionary.Values;
    ICollection<K> IDictionary<K, V>.Keys => _backingDictionary.Keys;
    IEnumerable<V> IOutValueSemiDictionary<V>.Values => Values;
    public void Add(K key, V value)
    {
        _backingDictionary.Add(key, value);
    }
    public void Add(KeyValuePair<K, V> item)
    {
        _backingDictionary.Add(item);
    }
    public void Clear()
    {
        _backingDictionary.Clear();
    }
    public bool Contains(KeyValuePair<K, V> item)
    {
        return _backingDictionary.Contains(item);
    }
    public bool ContainsKey(K key)
    {
        return _backingDictionary.ContainsKey(key);
    }
    public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
    {
        _backingDictionary.CopyTo(array, arrayIndex);
    }
    public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
    {
        return _backingDictionary.GetEnumerator();
    }
    public bool Remove(K key)
    {
        return _backingDictionary.Remove(key);
    }
    public bool Remove(KeyValuePair<K, V> item)
    {
        return _backingDictionary.Remove(item);
    }
    public ISuccessTuple<V> TryGetValue(K key)
    {
        bool wasSuccessful = _backingDictionary.TryGetValue(key, out V v);
        return new SuccessTupleImpl<V>(wasSuccessful, v);
    }
    public bool TryGetValue(K key, out V value)
    {
        return _backingDictionary.TryGetValue(key, out value);
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable)_backingDictionary).GetEnumerator();
    }
}

将字典包装在此类中后,可以将其用作常规字典,也可以用作定义的任何协变/逆变片段接口类型。

考虑是否

真的有必要在object之间进行转换。我开始走这条路,偶然发现了这篇文章,然后意识到我可以通过泛型而不是转换来实现我需要的东西。例如;

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

这段代码要干净得多,并且比其他答案中显示的等效转换代码更快。

是 可以将

FieldInfo转换为Dictionary,如下所示

这是我在代码中使用的一个例子

Dictionary<string, string> 
                GetTheDict = FilesAndPaths.GetType()
                             .GetFields()
                             .Where(f => f.Name.Equals(pLoadFile))
                             .Select(f => (Dictionary<string, string>)f.GetValue(FilesAndPaths))
                             .Single();

我遇到了同样的情况,并创建了一个WrapperDictionary类和一个强制转换扩展方法。如果您还想对其进行操作(例如删除项目),发现它很有帮助。

例:

        const string TestKey1 = "Key";
        const string TestValue1 = "Value";
        var dict = new Dictionary<string, string>();
        dict.Add(TestKey1, TestValue1);
        var wrapper = dict.CastDictionary<string, string, string, object>();
        wrapper.Remove(TestKey1);

法典:

public class DictionaryWrapper<TKeyTarget, TValueTarget, TKeySource, TValueSource> : IDictionary<TKeyTarget, TValueTarget>
{
    #region types
    private class EnumeratorWrapper : IEnumerator<KeyValuePair<TKeyTarget, TValueTarget>>
    {
        private readonly IEnumerator<KeyValuePair<TKeySource, TValueSource>> _enumerator;
        private readonly DictionaryWrapper<TKeyTarget, TValueTarget, TKeySource, TValueSource> _dictionaryWrapper;
        public EnumeratorWrapper(IEnumerator<KeyValuePair<TKeySource, TValueSource>> enumerator, DictionaryWrapper<TKeyTarget, TValueTarget, TKeySource, TValueSource> dictionaryWrapper)
        {
            _enumerator = enumerator;
            _dictionaryWrapper = dictionaryWrapper;
        }
        public void Dispose()
        {
            _enumerator.Dispose();
        }
        public bool MoveNext()
        {
            return _enumerator.MoveNext();
        }
        public void Reset()
        {
            _enumerator.Reset();
        }
        public KeyValuePair<TKeyTarget, TValueTarget> Current => _dictionaryWrapper._kvpSourceToTargetFunc(_enumerator.Current);
        object IEnumerator.Current => Current;
    }
    #endregion
    #region fields
    private readonly IDictionary<TKeySource, TValueSource> _dictionary;
    private readonly Func<TKeySource, TKeyTarget> _keySourceToTargetFunc;
    private readonly Func<TKeyTarget, TKeySource> _keyTargetToSourceFunc;
    private readonly Func<TValueSource, TValueTarget> _valueSourceToTargetFunc;
    private readonly Func<TValueTarget, TValueSource> _valueTargetToSourceFunc;
    private readonly Func<KeyValuePair<TKeySource, TValueSource>, KeyValuePair<TKeyTarget, TValueTarget>> _kvpSourceToTargetFunc;
    private readonly Func<KeyValuePair<TKeyTarget, TValueTarget>, KeyValuePair<TKeySource, TValueSource>> _kvpTargetToSourceFunc;
    #endregion
    #region Construction
    public DictionaryWrapper(
        IDictionary<TKeySource, TValueSource> dict, 
        Func<TKeySource, TKeyTarget> keySourceToTargetFunc = null,
        Func<TKeyTarget, TKeySource> keyTargetToSourceFunc = null, 
        Func<TValueSource, TValueTarget> valueSourceToTargetFunc = null, 
        Func<TValueTarget, TValueSource> valueTargetToSourceFunc = null)
    {
        _dictionary = dict;
        _keySourceToTargetFunc = keySourceToTargetFunc ?? (i => (TKeyTarget) (object) i);
        _keyTargetToSourceFunc = keyTargetToSourceFunc ?? (i => (TKeySource) (object) i);
        _valueSourceToTargetFunc = valueSourceToTargetFunc ?? (i => (TValueTarget) (object) i);
        _valueTargetToSourceFunc = valueTargetToSourceFunc ?? (i => (TValueSource) (object) i);
        _kvpSourceToTargetFunc = 
            kvp => new KeyValuePair<TKeyTarget, TValueTarget>(_keySourceToTargetFunc(kvp.Key), _valueSourceToTargetFunc(kvp.Value));
        _kvpTargetToSourceFunc = 
            kvp => new KeyValuePair<TKeySource, TValueSource>(_keyTargetToSourceFunc(kvp.Key), _valueTargetToSourceFunc(kvp.Value));
    }
    #endregion
    #region Interface Members
    public IEnumerator<KeyValuePair<TKeyTarget, TValueTarget>> GetEnumerator()
    {
        return new EnumeratorWrapper(_dictionary.GetEnumerator(), this);
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    public void Add(KeyValuePair<TKeyTarget, TValueTarget> item)
    {
        _dictionary.Add(_kvpTargetToSourceFunc(item));
    }
    public void Clear()
    {
        _dictionary.Clear();
    }
    public bool Contains(KeyValuePair<TKeyTarget, TValueTarget> item)
    {
        return _dictionary.Contains(_kvpTargetToSourceFunc(item));
    }
    public void CopyTo(KeyValuePair<TKeyTarget, TValueTarget>[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }
    public bool Remove(KeyValuePair<TKeyTarget, TValueTarget> item)
    {
        return _dictionary.Remove(_kvpTargetToSourceFunc(item));
    }
    public int Count => _dictionary.Count;
    public bool IsReadOnly => _dictionary.IsReadOnly;
    public bool ContainsKey(TKeyTarget key)
    {
        return _dictionary.ContainsKey(_keyTargetToSourceFunc(key));
    }
    public void Add(TKeyTarget key, TValueTarget value)
    {
        _dictionary.Add(_keyTargetToSourceFunc(key), _valueTargetToSourceFunc(value));
    }
    public bool Remove(TKeyTarget key)
    {
        return _dictionary.Remove(_keyTargetToSourceFunc(key));
    }
    public bool TryGetValue(TKeyTarget key, out TValueTarget value)
    {
        var success = _dictionary.TryGetValue(_keyTargetToSourceFunc(key), out TValueSource result);
        value = success ? _valueSourceToTargetFunc(result) : default;
        return success;
    }
    public TValueTarget this[TKeyTarget key]
    {
        get => _valueSourceToTargetFunc(_dictionary[_keyTargetToSourceFunc(key)]);
        set => _dictionary[_keyTargetToSourceFunc(key)] = _valueTargetToSourceFunc(value);
    }
    public ICollection<TKeyTarget> Keys => _dictionary.Keys.Select(k => _keySourceToTargetFunc(k)).ToList();
    public ICollection<TValueTarget> Values => _dictionary.Values.Select(v => _valueSourceToTargetFunc(v)).ToList();
    #endregion
}
public static class DictionaryWrapperExtensions
{
    public static IDictionary<TKeyCasted, TValueCasted> CastDictionary<TKey, TValue, TKeyCasted, TValueCasted>(this IDictionary<TKey, TValue> dictionary)
    {
        return new DictionaryWrapper<TKeyCasted,TValueCasted,TKey,TValue>(dictionary);
    }
}