Reputation: 26267
Id like to be able to work with Dictionaries in generic methods. What Im looking for is a way to get the Type from Key and Value of the dictionary sent to the generic extension method LoadProperty.
This is what I've done so far.
I call the method as an extension
entityObject.LoadProperty<Dictionary<string, int>>("CartonThreshold")
//entityObject.LoadProperty<Dictionary<string, double>>("CartonThreshold")
// ...
public static T LoadProperty<T>(this EntityObject entity, string name) where T : new()
{
EntityObjectProperty prop = entity.Properties.Single(x => x.Name.Equals(name));
// If request dictionary
if (typeof(T).GetInterface(typeof(IDictionary<,>).Name) != null || typeof(T).Name.Contains("IDictionary"))
{
var dictionaryInstance = (IDictionary)new T();
// Just for testing
var a = dictionaryInstance.Keys.GetType().Name;
var b = dictionaryInstance.Values.GetType().Name;
var key = (T) Convert.ChangeType(prop.Name, typeof (T));
var value = (T) Convert.ChangeType(prop.Value, typeof (T));
dictionaryInstance.Add(key, value);
return (T) dictionaryInstance;
}
// default
return (T)Convert.ChangeType(prop.Value, typeof(T));
}
The goal is to return a correctly typed dictionary
Upvotes: 1
Views: 4734
Reputation: 57202
If I understood your question correctly, what you would like to do is define your method like this:
public static T LoadProperty<T,TKey,TValue>(this EntityObject entity, string name)
where T : IDictionary<TKey,TValue>, new() {
EntityObjectProperty prop = entity.Properties.Single(x => x.Name.Equals(name));
T t = new T();
t.Add((TKey)prop.Name, TValue(prop.Value));
return t;
}
Then call it with
Dictionary<string,int> property = entityObject.LoadProperty<Dictionary<string,int>, string, int>("test");
The bad thing is that you have to specify all the generic parameters each time...
If it's ok for your concrete case, it would be much simpler like this:
public static IDictionary<TKey,TValue> LoadProperty<TKey,TValue>(this EntityObject entity, string name) {
EntityObjectProperty prop = entity.Properties.Single(x => x.Name.Equals(name));
var dict = new Dictionary<TKey,TValue>();
dict.Add((TKey)prop.Name, TValue(prop.Value));
return dict;
}
EDIT: after your clarification, what about telling the two cases apart by the number of generic arguments?
public static IDictionary<TKey,TValue> LoadProperty<TKey,TValue>(this EntityObject entity, string name) {
var d = new Dictionary<TKey,TValue>();
// add value from EntityObject
return t;
}
public static TValue LoadProperty<TValue>(this EntityObject entity, string name) {
TValue ret;
// get value from EntityObject
return ret;
}
Upvotes: 0
Reputation: 20950
To get it working with almost anything, you could pass a function to create whatever return type you want:
public static T LoadProperty<TKey,TValue,T>(this EntityObject entity, string name, Func<TKey,TValue,T> createWhatever)
{
EntityObjectProperty prop = entity.Properties.Single(x => x.Name.Equals(name));
return createWhatever((TKey)prop.Name, (TValue)prop.Value);
}
This would be a bit more verbose at the calling, though.
Upvotes: 0
Reputation: 26267
What I was looking for was the GetGenericArguments() extension of GetType()
The method below will return what I need.
public static T LoadProperty<T>(this EntityObject entity, string name) where T : new()
{
EntityObjectProperty prop = entity.Properties.Single(x => x.Name.Equals(name));
try
{
// If request dictionary
if (typeof(T).GetInterface(typeof(IDictionary<,>).Name) != null || typeof(T).Name.Contains("IDictionary"))
{
var dictionaryInstance = (IDictionary)new T();
var typing = dictionaryInstance.GetType().GetGenericArguments();
Type keyType = typing[0];
Type valueType = typing[1];
// dictionary fallback, set to default of the valuetype if null
object value = prop.Value != null ? Convert.ChangeType(prop.Value, valueType) : Activator.CreateInstance(valueType);
var key = Convert.ChangeType(prop.Name, keyType);
dictionaryInstance.Add(key, value);
return (T)dictionaryInstance;
}
if (prop.Value != null)
{
// default
return (T)Convert.ChangeType(prop.Value, typeof(T));
}
}
catch { }
return default(T);
}
This way I can call the method like this
// Will return a typed dictionary with the EntityObjectProperty name as key and the EntityObjectProperty Value as value
entityObject.LoadProperty<Dictionary<string, int>>("CartonThreshold")
// Will return a string of the EntityObjectProperty Value
entityObject.LoadProperty<string>("CartonThreshold")
// Will return an Int of the EntityObjectProperty Value
entityObject.LoadProperty<int>("CartonThreshold")
Upvotes: 1
Reputation: 7192
Why don't you just define your method like this:
public static IDictionary<K, V> LoadProperty<K, V>(this EntityObject entity, string name)
Upvotes: 0