6

I would like to do some similar process within a loop like this by calling a generic method with different types.

AAA, BBB are all classes. CreateProcessor is a generic method in the class of MyProcessor.

new List<Type> {typeof (AAA), typeof (BBB)}.ForEach(x =>
{
    var processor = MyProcessor.CreateProcessor<x>(x.Name);
    processor.process();
});

This doesn't compile, I got the error saying Cannnot resolve symbol x.

Technically, how to achieve it? (I know the strategy pattern is better...)

zs2020
  • 53,766
  • 29
  • 154
  • 219
  • What are `AAA` and `BBB` and `MyClass`? Do you have code for them? Do they implement interfaces which expose the Process method? – Faraday Jan 16 '14 at 00:15
  • @Vijay How would you like to infer `AAA` from passing `typeof(AAA)`? – MarcinJuraszek Jan 16 '14 at 00:41
  • Please do not roll back my question! – zs2020 Jan 16 '14 at 00:48
  • @MarcinJuraszek - I added a little console app to demonstrate what I was trying to explain. See my edit, hopefully you will understand what I was trying to say when I said it would be inferred. What I really meant is that you could delay the check until runtime and remove the error - The correct overload would still be called. – Faraday Jan 16 '14 at 01:07
  • @zsong - Check out Edit 2 in my post, I think that's what you were looking for... If, tell me what that doesn't do correctly and I'll fix it up for you... :) – Faraday Jan 16 '14 at 01:35
  • possible duplicate of [How to use reflection to call generic Method?](http://stackoverflow.com/questions/232535/how-to-use-reflection-to-call-generic-method) – nawfal Jan 17 '14 at 15:23
  • @zsong - Did you ever check my last update? I'm pretty sure it does exactly what you wanted... :) – Faraday Jan 21 '14 at 17:03

4 Answers4

8

Reflection is required to deal with the Type class:

    new List<Type> { typeof(AAA), typeof(BBB) }.ForEach(x => {
        var type = typeof(MyClass<>).MakeGenericType(x);
        dynamic processor = Activator.CreateInstance(type, x.Name);
        processor.process();
    });
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    I like that you used a dynamic here, seems like a perfect use for it! – ohmusama Jan 16 '14 at 00:22
  • Sorry, I updated my question. I intended to call a generic method actually. – zs2020 Jan 16 '14 at 00:24
  • 1
    @Zsong, look here: [How to use reflection to call generic Method?](http://stackoverflow.com/questions/232535/how-to-use-reflection-to-call-generic-method) –  Jan 16 '14 at 00:31
7

Sorry, I updated my question. I intended to call a generic method actually.

var method = typeof(MyProcessor).GetMethod("CreateProcessor", new Type[] { typeof(string) });
new List<Type> { typeof(AAA), typeof(BBB) }.ForEach(x =>
{
    dynamic processor = method.MakeGenericMethod(x).Invoke(null, new[] { x.Name });
    processor.process();
});
MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
  • Thanks! Somehow when I pass in multiple parameters for the 2nd parameter of this line `.Invoke(null, new[] { x.Name, xxx, cccc });` I got an error `no best type found for the implicitly-typed array`. I declared in the first line btw – zs2020 Jan 16 '14 at 00:47
  • use `new object[] { }` – MarcinJuraszek Jan 16 '14 at 00:51
  • Thx. I think in my case, it is very complicated. I have both `CreateProcessor(string n)` and `CreateProcessor(string n)` defined and I got this error "Ambiguous match found." – zs2020 Jan 16 '14 at 01:02
  • I am going to mark your answer as the correct answer. I don't think it is possible to get the generic method only. – zs2020 Jan 16 '14 at 01:04
  • What do you mean by "get the generic method" – Faraday Jan 16 '14 at 01:05
  • Yeah, it should work even if you have another non-generic method with the same name and set of parameters. – MarcinJuraszek Jan 16 '14 at 01:07
  • You use of the term generic and non-generic confuse me. Surely an object cannot have two methods with the exact same signature, so it's not clear to me what you are saying. – Faraday Jan 16 '14 at 01:07
  • @Vijay you can have both `public void Test(string input)` and `public void Test(string input)` within the same class. First one is generic method second one is non-generic method. And they both have the same name and the same set of parameters. – MarcinJuraszek Jan 16 '14 at 01:09
0

This is how you would create a new variable using only type information, by using dynamic you are able to call any method which you know exists for all of the types. I would suggest (assuming these types are your own classes) you implement an interface baseclass or something for these if possible, it simplify your quite a lot...

new List<Type> { typeof(string), typeof(int) }.ForEach(x =>
{
    dynamic processor = Activator.CreateInstance(x);
    processor.ToString();
    processor.CallAnyMethodHere();
    processor.Process();
});

Edited code - Adding a clear example

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

public class mainClass
{
    public static void Main(string[] args)
    {
        new List<Type> { typeof(StringBuilder), typeof(Int64) }.ForEach(x =>
        {
            dynamic instance = Activator.CreateInstance(x);

            DoSomething(instance);
        });

        Console.ReadKey();
    }

    public static void DoSomething(StringBuilder stringBuilder)
    {
        Console.WriteLine("StringBuilder overload");
    }


    public static void DoSomething(Int64 int64)
    {
        Console.WriteLine("Int64 overload");
    }
}

Edit 2 - Only call generic method

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Linq;

public class mainClass
{
    public static void Main(string[] args)
    {
        new List<Type> { typeof(StringBuilder), typeof(Int64) }.ForEach(x =>
        {
            var methodInfoArray = typeof(mainClass).GetMethods();
            var methodInfo = methodInfoArray.First(mi => mi.Name == "DoSomething" && mi.IsGenericMethodDefinition);
            var genericMethod = methodInfo.MakeGenericMethod(new Type[] { x });
            var blah = genericMethod.Invoke(null, new object[] { "Hello" }) as MethodInfo;
        });

        Console.ReadKey();
    }

    public static void DoSomething<T>(string variable)
    {
        Console.WriteLine("DoSomething<T> " + typeof(T) + " overload - " + variable);
    }

    public static void DoSomething(string variable)
    {
        Console.WriteLine("DoSomething - " + variable);
    }

}
Faraday
  • 2,904
  • 3
  • 23
  • 46
  • Woah, that got downvoted quick, anyone care to explain? – Faraday Jan 16 '14 at 00:19
  • 1
    Look at Hans' answer. You missed the important part of how the OP should call the process() method... –  Jan 16 '14 at 00:24
  • @elgonzo - He edited his post a number of times... When I wrote my answer he didn't specify it must call that and I didn't want to use a dynamic. I didn't copy and paste anything, but if not having the `process` method means I need to use a dynamic then I sure as hell will to get rid of some of these downvotes... Even though he said a generic method, to me meaning any method, which is why I chose tostring... – Faraday Jan 16 '14 at 00:28
  • I removed my comment regarding c'n'p after checking your edit history. Sorry about the premature judgement. Anyway, regarding the rest that could be the only suitable explanation that comes to my mind (no, i didn't downvote). –  Jan 16 '14 at 00:34
  • haha, fair enough. I was just surprised, within 10 seconds of clicking submit I got three downvotes... I got scared I was being attacked! I still don't think we've answered the OP, he's edited his question again... – Faraday Jan 16 '14 at 00:35
0

You have to use reflection to create a generic object like that. This page from Microsoft has an excellent rundown on what you need to do: How to: Examine and Instantiate Generic Types with Reflection

itsme86
  • 19,266
  • 4
  • 41
  • 57