简体   繁体   中英

Switch between types including interfaces

I have a set of overloads for a get function. Each one have a different input type but basically the same signature schema (as follow):

string GetPropertyValue(int propId, string defaultValue)
bool GetPropertyValue(int propId, bool defaultValue)
int GetPropertyValue(int propId, int defaultValue)
IEnumerable<string> GetPropertyValue(int propId, IEnumerable<string> defaultValues)
IEnumerable<bool> GetPropertyValue(int propId, IEnumerable<bool> defaultValues)
IEnumerable<int> GetPropertyValue(int propId, IEnumerable<int> defaultValues)

I am working on simplifying the API into a single generic method (as follow):

T GetPropertyValue<T>(int propId , T defaultValue)

To implement such method, I tried to switch on the type on the default value using a dictionary ( inspired by this answer ):

var actionDico = new Dictionary<Type, System.Action>
{
    /* since the type of `defaultValue` is `T`, I cannot use `(bool)defaultValue` for example
       therefore casting to (object) before to escape Cast operator restriction.
       Will not fail as the key of the dictionary is a matching type */
    {typeof(bool), () => dob.GetPropertyValue(propId, (bool)(object)defaultValue)},
    {typeof(int), () => dob.GetPropertyValue(propId, (int)(object)defaultValue)},
    {typeof(string), () => dob.GetPropertyValue(propId, (string)(object)defaultValue)}
}

With concrete types, the previous implementation is perfectly fine (at least in my case). The call will be done using actionDico[typeof(T)](); .

Having the following within the dictionary is fine:

{typeof(IEnumerable<int>), () => dob.GetPropertyValue(propId, (IEnumerable<int>)(object)defaultValue)},

But the call is usually done using an object which implement IEnumerable<int> (like List<int> ). In such case calling actionDico[typeof(T)](); is looking for List<int> within the keys collection, not IEnumerable<int> .

I am trying to avoid reflection (and will keep it as last resort). Is there a way similar to Type.IsAssignableFrom(Type) method for interfaces? In other words, I want to check wether the provided type implements IEnumerable<int> rather than being it.

You can't look the type up in the dictionary that way. You'll have to loop through the key-value pairs:

Type targetType = defaultValue.GetType();
foreach (var pair in dictionary)
{
    if (pair.Key.IsAssignableFrom(targetType))
    {
        // Use pair.Value
    }
}

However, at this point you've effectively just got a List<Tuple<Type, Action>> rather than a dictionary, in terms of how you're using it... So you could instead use:

List<Tuple<Type, Action>> actions = new List<Tuple<Type, Action>>
{
    Tuple.Create(typeof(bool), () => dob.GetPropertyValue(propId, (bool) (object)defaultValue),
    Tuple.Create(typeof(int), () => dob.GetPropertyValue(propId, (int) (object)defaultValue),
    // etc
};

... and then just use Item1 and Item2 in the loop earlier.

You can use the as operator

var isEnumerable = defaultValue as IEnumerable;
if (isEnumerable != null)
{

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM