Anthony
Anthony

Reputation: 5226

call a generic method with an object of an unknown type

I have this method

public string DictionaryToString<T, U>(Dictionary<T, U> dict)
{
  var valueStrings = dict.Select(x => x.Key.ToString() + ": " + x.Value.ToString());
  return String.Join("\n", valueStrings);
}

And I have this object that I want to pass into it

if ((value !=null) && value.GetType().IsGenericType &&
  value.GetType().GetGenericTypeDefinition() == typeof (Dictionary<,>))
{
   var castValue = value as Dictionary<,>; // this cast does not work
   return DictionaryToString(castValue);
}
else
{
  return value.ToString();
}

I can use reflection code like this in .Net 4.5

var targetMethodInfo = typeof(MyType).GetMethod("DictionaryToString");
var valueTypeArgs = value.GetType().GenericTypeArguments;
var genericMethod = targetMethodInfo.MakeGenericMethod(valueTypeArgs);
var result = genericMethod.Invoke(this, new[] {value });
return result.ToString();

ButType.GenericTypeArguments is new in .Net 4.5. So how can I do that cast in .Net 4.0?

Upvotes: 0

Views: 896

Answers (5)

Adam Houldsworth
Adam Houldsworth

Reputation: 64487

You are only calling ToString on the key and value, so simply have this method take an IDictionary (non-generic), you are not using anything in there that is type-specific to T or U.

You can then simply cast all arguments to IDictionary:

var d = arg as IDictionary;

if (d != null)
{
    var res = DictionaryToString(d);
}

You may also need to amend the DictionaryToString implementation:

static string DictionaryToString(IDictionary d)
{
    var vals = new List<string>();

    foreach (DictionaryEntry de in d)
    {
        vals.Add(de.Key.ToString() + ": " + de.Value.ToString());
    }

    return String.Join("\n", vals);
}

Alternatively, if you really want to use LINQ, you could try casting to dynamic (it isn't possible to cast to anything else as this could be a generic dictionary (KeyValuePair<>) or non-generic hashtable (DictionaryEntry)):

var valueStrings = d.Cast<dynamic>().Select(de => de.Key.ToString() + ": " + de.Value.ToString());
return string.Join("\n", valueStrings);

This basically "duck types" the existence of the Key and Value properties.

Upvotes: 3

qxn
qxn

Reputation: 17584

This sounds like it might be a case for the old System.Collections namespace:

    private static string DictionaryToString(IDictionary dict) {
        if (null == dict) throw new ArgumentNullException("dict");

        var valueStrings = new List<string>();
        foreach (DictionaryEntry item in dict) {
            valueStrings.Add(item.Key + ": " + item.Value);
        }
        return string.Join("\n", valueStrings.ToArray());
    } 

    private static string Test(object value) {

        var dict = value as IDictionary;
        if (dict != null) {
            return DictionaryToString(dict);
        }

        if (value == null) {
            return null;
        }

        return value.ToString();
    }

    private static void Main(string[] args) {

        var aDictionary = new Dictionary<int, string> {
            { 1, "one" },
            { 2, "two" },
            { 3, "three" }
        };

        Console.WriteLine(Test(aDictionary));

        var anotherDictionary = new Dictionary<string, object> {
            { "one", 1 },
            { "two", "2" },
            { "three", new object() }
        };

        Console.WriteLine(Test(anotherDictionary));

        Console.ReadLine();
    }

Reasoning:

The non-generic IDictionary will be a collection of key-value pairs in which the key is an object and the value is an object. All instances of object support ToString, so all keys and values of the collection can be converted to a string without knowing their specific types.

The reason why this does not work:

var castValue = value as Dictionary<,>

is because the generic type Dictionary<TKey, TValue> requires 2 type arguments. Without those type arguments, the collection is not generic. You'd be better off using the non-generic IDictionary if you do not know the key or value types at compile-time.

Upvotes: 1

Hamlet Hakobyan
Hamlet Hakobyan

Reputation: 33381

Why not?

if ((value !=null) && value.GetType().IsGenericType &&
       value.GetType().GetGenericTypeDefinition() == typeof (Dictionary<,>))
{
   return DictionaryToString(castValue);
}
else
{
  return value.ToString();
}

Upvotes: 0

Douglas
Douglas

Reputation: 54887

You could cast to dynamic in .NET 4.0. The prior typeof(Dictionary<,>) check will ensure that you won't get runtime errors.

var castValue = value as dynamic;
return DictionaryToString(castValue);

Upvotes: 0

fsimonazzi
fsimonazzi

Reputation: 3013

GetGenericTypeDefinition is available for previous versions http://msdn.microsoft.com/en-us/library/system.type.getgenerictypedefinition(v=vs.100).aspx. I think the 4.5 page on MSDN is missing the "Other versions" dropdown.

Upvotes: 0

Related Questions