Is it possible to cast a Dictionary<string, Anything>
to a consistent intermediate generic type? So I would be able to cast <string, string>
, <string, bool>
, <string, int>
, <string, anything>
all to the same type of dictionary?
I am working on a project that is using heavy reflection and I need to be able to process DIctionary types as such:
FieldInfo field = this.GetType().GetField(fieldName);
Dictionary<string, Object> dict = (Dictionary<string, Object>)field.GetValue(this);
The above code is what I currently have and the program always fails at the cast from field.GetValue to the generic Dictionary<string, Object>
.
Is there a way to do this? Or should I just figure out a different way to process these Dictionaries?
Any help would be greatly appreciated.
As stated in this answer:
it's not true that a
Dictionary<string, bool>
is aDictionary<string,object>
Let me explain why. It all has to do with covariance and contravariance in C#. In summary, neither the key type K
nor the value type V
in Dictionary<K, V>
is either purely an input parameter or an output parameter across the entire dictionary. So neither generic type can be cast to a weaker or a stronger type.
If you cast to a weaker type, then you break inputs. For example the Add
function which expects types K, V
or stronger cannot accept supertypes of either of K, V
. If you cast to a stronger type, you break outputs. For example, the indexer property returns a type V
. How can we cast this safely to a sub-type of V
, knowing only that the original type is V
? We can't.
There is a way to allow strongly-typed casting, but only on partial fragments of the original dictionary interface which are consistent in terms of covariance/contravariance of generic parameters. This solution uses a wrapper type which implements a bunch of partial interfaces. Each partial interface has a specific type of combination of co/contra-variance of the key/value type. Then we combine all these partial interfaces into a master interface and implement that through a backing object which is a regular dictionary. Here goes. First, the interfaces:
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; }
}
Note: we don't have to cover all combinations of in/out here for the generic parameters, and also note that some of the interfaces only need a single generic parameter.The reason is some combinations of covariance/contravariance don't have any associated methods, so there's not need for corresponding types. And note that I'm leaving out the KeyValuePair<K, V>
type- you'd need to do a similar trick on this if you wanted to include it, and it doesn't seem worth it at this stage.
So, next, the union of all those interfaces:
public interface IVariantDictionary<K, V> : IInKeyInValueSemiDictionary<K, V>, IInKeyOutValueSemiDictionary<K, V>,
IOutKeySemiDictionary<K>, IDictionary<K, V>
{ }
And then, the wrapper class:
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();
}
}
Once you wrap a dictionary in this class, you can then use it either as a regular dictionary, or as any of the covariant/contravariant fragment interface types defined.
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));
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.
Is this helping you ?
Dictionary<a, b> output =
input.ToDictionary(item => item.Key, item => (SomeType)item.Value);
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With