-2

I have the following function. The function will always be passed the parameter of k which can be converted to keyType.

void F(object k, Type keyType) // keyType is from runtime. cannot be generic
{
    object kvalue = Convert.ChangeType(k, keyType); //boxed int as expected
    dynamic hs = Activator.CreateInstance(typeof(HashSet<>).MakeGenericType(keyType)); 
    hs.Add(kvalue); // Runtime error
    //....
}

// Setup for testing, not real usage.
object o = 10m; // decimal
var t = typeof(int); // will get the type at runtime. 

// Call the function
F(o, t);

However, the above code will get the runtime error of:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'The best overloaded method match for 'System.Collections.Generic.HashSet<int>.Add(int)' has some invalid arguments'

Changing

dynamic hs = Activator.CreateInstance(typeof(HashSet<>).MakeGenericType(keyType));

to

//dynamic hs = Activator.CreateInstance(typeof(HashSet<dynamic>));
var hs = new HashSet<dynamic>();

can resolve the problem. Is there any other way?

ca9163d9
  • 27,283
  • 64
  • 210
  • 413

1 Answers1

7

Convert.ChangeType returns an instance of object. There's no Add(object) method defined on HashSet<int>, only Add(int).

Try the following using expression:

void F(object k, Type keyType)
{
    var hsType = typeof(HashSet<>).MakeGenericType(keyType);
    object hs = Activator.CreateInstance(hsType);
    var kvalue = Convert.ChangeType(k, keyType);

    Expression.Lambda<Action>(
        Expression.Call(
            Expression.Constant(hs, hsType),
            "Add",
            Type.EmptyTypes,
            Expression.Constant(kvalue, keyType))
        ).Compile()();
}
weichch
  • 9,306
  • 1
  • 13
  • 25