Generic Method Executed with a runtime type

31,585

Solution 1

You can, but it involves reflection, but you can do it.

typeof(ClassExample)
    .GetMethod("DoSomething")
    .MakeGenericMethod(p.DisplayType)
    .Invoke(this, new object[] { p.Name, p.Value });

This will look at the top of the containing class, get the method info, create a generic method with the appropriate type, then you can call Invoke on it.

Solution 2

this.GetType().GetMethod("DoSomething").MakeGenericMethod(p.Value.GetType()).Invoke(this, new object[]{p.Name, p.Value});

Should work.

Solution 3

Generics types cannot be specified at runtime the way you'd like to here.

The simplest options would be to add a non-generic overload of DoSomething, or simply call DoSomething<object> and ignore p.DisplayType. Unless SendToDatabase depends on the compile-time type of value (and it probably shouldn't), there should be nothing wrong with giving it an object.

If you can't do those, you'll have to call DoSomething using reflection, and you'll take a big performance hit.

Solution 4

First we need to convert p.Value to the right type, since even if we know the type at compile time we can't pass the string straight to the method...

DoSomething<Int32>( "10" ); // Build error

For simple numeric types and DateTime, we can use

object convertedValue = Convert.ChangeType(p.Value, p.DisplayType);

Now we can use reflection to invoke the required generic method...

typeof(ClassExample)
    .GetMethod("DoSomething")
    .MakeGenericMethod(p.DisplayType)
    .Invoke(this, new object[] { p.Name, convertedValue });

Solution 5

Strictly saying you can use MethodInfo.MakeGenericMethod for this.

But I recommend to change DoSomething to non-generic form instead, since it is not evident whether it really should be generic.

Share:
31,585
Sarit
Author by

Sarit

C# &amp; Java Software Developer

Updated on October 23, 2020

Comments

  • Sarit
    Sarit over 3 years

    I have the following code:

     public class ClassExample
    {
    
        void DoSomthing<T>(string name, T value)
        {
            SendToDatabase(name, value);
        }
    
        public class ParameterType
        {
            public readonly string Name;
            public readonly Type DisplayType;
            public readonly string Value;
    
            public ParameterType(string name, Type type, string value)
            {
                if (string.IsNullOrEmpty(name))
                    throw new ArgumentNullException("name");
                if (type == null)
                    throw new ArgumentNullException("type");
    
                this.Name = name;
                this.DisplayType = type;
                this.Value = value;
            }
        }
    
        public void GetTypes()
        {
            List<ParameterType> l = report.GetParameterTypes();
    
            foreach (ParameterType p in l)
            {
                DoSomthing<p.DisplayType>(p.Name, (p.DisplayType)p.Value);
            }
    
        }
    }
    

    Now, I know I cannot perform DoSomething() is there any other way to use this function?

  • Sarit
    Sarit over 14 years
    I just simplified the program for the explanation. This is actually the most elegant solution, believe it or not. :) thanks!
  • stevemegson
    stevemegson over 14 years
    Just one catch, p.Value is a string so the invoke will fail unless p.DisplayType happens to be typeof(string).
  • Andrew Bullock
    Andrew Bullock over 14 years
    shame this is the only solution, meh
  • Chris Patterson
    Chris Patterson over 14 years
    Well, I'm thinking that you can use dynamic in 4.0 and it will infer the correct generic argument type, but I haven't had a chance to verify it yet. Not that it is doing anything different under the covers than the above code, but maybe it is.
  • Engineer
    Engineer about 5 years
    About brevity: Can does not imply should.