3

I have a generic method declared as follow :

public void Duplicate<EntityType>() { ... }

So, generally to use it I simply need to do say :

myObject.Duplicate<int>()

But here, what I'd like to do is pass the type by a variable, but it doesn't work, here is how I try to do this :

Type myType = anObject.GetType();
myObject.Duplicate<myType>();

If someone can help me ?

Thank in advance.

Karnalta
  • 518
  • 1
  • 9
  • 24

4 Answers4

12

You have to use reflection, basically:

MethodInfo method = typeof(...).GetMethod("Duplicate");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(myObject, null);
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    One of these days I'm going to follow your advice to post then revise. Beaten to the punch once again for double checking a method name. :) +1 – Harry Steinhilber May 11 '11 at 09:17
  • Should do the trick, I will test it. Thank – Karnalta May 11 '11 at 09:45
  • Hey, @Jon, I know your answer dates back to about 5 years ago and maybe `MethodInfo.Invoke` method used to have an overload (if not the only one) that took one argument but right now that's not the case. We need to pass at least `null` if the method to invoke doesn't take any parameters. And there's another case where there might be overloads of the `Duplicate` method which will throw AmbiguousMatchException. I'm sure you know it but I just wanted to add my two cents anyway. – Mikayil Abdullayev Feb 23 '16 at 07:43
  • @MikayilAbdullayev: Added the `null` argument. – Jon Skeet Feb 23 '16 at 08:15
4

Where you have the type as a variable you don't really have a good candidate for generic methods, especially as your method returns nothing.

You would be better off with:

public void Duplicate(Type entityType) { ... }

Then your code becomes:

Type myType = anObject.GetType();
myObject.Duplicate(myType);

You can use Jon Skeet's (correct) answer to call your method by reflection, but I can't see what you gain by this method being generic.

The purpose of generics is to preserve the type - so that you don't have to box/unbox value types and so on.

However your duplicate method has no return (it's a void) and no input parameters. Essentially the only input is the type-parameter. This means that somewhere inside your generic method you're probably doing something like:

public void Duplicate<EntityType>() 
{
 ...
 Type inputType = typeof(EntityType); 
 ...
}

In that case you're not gaining anything by EntityType being a generic type-parameter rather than a regular input parameter, and you're adding the need for awkward and slow reflection when you don't know the input at compile time.

Keith
  • 150,284
  • 78
  • 298
  • 434
0

It's not quite clear of the exact function of Duplicate(), but the following example works when your type you want to pass in to Duplicate() is a class or an int.

Essentially, don't pass in a type, pass in an example object. This test method illustrates the call (two tests in one method)

    [TestMethod]
    public void TestMethod1()
    {
        var myObject = new AnObject { AString = "someString" };

        var myOtherObject = new AnotherObject();
        var newObject = myObject.Duplicate(myOtherObject);
        Assert.IsTrue(newObject.AString=="someString");

        var newObject2 = myObject.Duplicate(1);
        Assert.IsTrue(newObject2 is Int32);
    }

The Duplicate() method has a type parameter linked to the example object, but does not need to be called with the type given as a parameter, it infers it from the example object.

Here is the class with a Duplicate() method:

public class AnObject
{
    public string AString { get; set; }

    public T Duplicate<T>(T exampleObject)
        where T: new()
    {
        var newInstance = (T)Activator.CreateInstance<T>();

        // do some stuff here to newInstance based on this AnObject
        if (typeof (T) == typeof (AnotherObject))
        {
            var another = newInstance as AnotherObject;
            if(another!=null)
                another.AString = this.AString;
        }

        return newInstance;
    }
}

To complete the picture, this is the AnotherObject class.

public class AnotherObject
{
    public string AString { get; set; }
}

This code may have some problems inside the Duplicate() method when the type passed in is a value type like int. It depends on what you want Duplicate() to do with the int (you may have to be pass it in as a nullable int).

It works well for reference types (classes).

Ackroydd
  • 1,482
  • 1
  • 13
  • 14
  • Actually, if you're going down the generic route, I think's Keith's reply is a better version than mine. No example type is required. I'm assuming it works, and that you don't need a return value. – Ackroydd Dec 13 '12 at 14:10
  • I'm assuming that Duplicate() is intended to return a clone of the type passed in, but your question indicates there is no return value. – Ackroydd Dec 13 '12 at 14:28
0

You could use a standard method taking a Type as an argument:

myobject.Duplicate(myType);

or you can try with reflection, something like:

System.Reflection.MethodInfo mi = typeof(TypeOfMyObject).GetMethod("Duplicate<>");
MethodInfo constructedMethodInfo = mi.MakeGenericMethod(new type[] {myType});
constructedMethodInfo.Invoke(myObject, new object[] {});
Álvaro Iradier
  • 295
  • 2
  • 12