13

是否可以将 aDictionary<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);

上面的代码是我目前拥有的,程序总是在从 field.GetValue 转换为 generic 时失败Dictionary<string, Object>

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

任何帮助将不胜感激。

4

8 回答 8

16

按照AakashM 的回答,Cast 似乎没有打球。不过,您可以通过使用一些辅助方法来解决它:

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 的鸭子很方便。

于 2012-04-18T10:20:46.030 回答
14

这对你有帮助吗?

Dictionary<a, b> output =
   input.ToDictionary(item => item.Key, item => (SomeType)item.Value);
于 2012-04-18T09:30:58.483 回答
10

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

然而,我们可以做的是强制转换为非泛型IDictionary(所有Dictionary<,>s 都实现),然后使用它来构造具有相同值的new : 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 的回答所做的那样

于 2012-04-18T09:51:37.990 回答
2

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

/// <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));
于 2020-05-21T22:07:34.783 回答
1

问题说明:差异

本答案所述:

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

让我解释一下为什么。这一切都与C# 中的协变和逆变有关。总之,在整个字典中,键类型和值类型都不是纯粹的输入参数或输出参数K。因此,任何泛型类型都不能转换为更弱或更强的类型。VDictionary<K, V>

如果您转换为较弱的类型,那么您会破坏输入。例如Add,期望类型K, V或更强大的函数不能接受任何一个的超类型K, V。如果您转换为更强的类型,则会破坏输出。例如, indexer 属性返回一个 type 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; }
}

注意:对于泛型参数,我们不必在这里涵盖所有的输入/输出组合,还要注意一些接口只需要一个泛型参数。原因是一些协变/逆变的组合没有任何关联的方法,因此不需要相应的类型。请注意,我忽略了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();
    }
}

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

于 2020-07-03T17:25:42.873 回答
0

是的,可以将其FieldInfo转换Dictionary为如下

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

Dictionary<string, string> 
                GetTheDict = FilesAndPaths.GetType()
                             .GetFields()
                             .Where(f => f.Name.Equals(pLoadFile))
                             .Select(f => (Dictionary<string, string>)f.GetValue(FilesAndPaths))
                             .Single();
于 2013-09-04T08:45:53.867 回答
0

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

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

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

于 2016-05-26T14:58:06.107 回答
0

我有同样的情况,并创建了一个 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);
    }
}
于 2020-06-05T08:46:00.860 回答