I have these classes:
public static class A{
...
public C Run<T>(string something)
{
...
}
}
public static class B{
...
public void Exec<T>(Type type)
{
MethodInfo method = typeof(A).GetMethod("Run");
MethodInfo generic = method.MakeGenericMethod(type);
var result = generic.Invoke(null, new object[] { "just a string" });
// bad call in next line
result.DoSomething();
}
}
public class C{
...
public void DoSomething(){}
}
How to convert result to type for call DoSomething method? And how simpler to call generic method using type variable?
How to convert result to type for call DoSomething method?
You cannot do that statically, because your code does not know the type at compile time, and the object is already of the correct type. One way to do it in .NET 4.0 and later is to use dynamic instead of object for the result, like this:
dynamic result = generic.Invoke(null, new object[] { "just a string" });
result.DoSomething(); // This will compile
You can do it like this only if you are 100% certain that the DoSomething() method is going to be there at runtime. Otherwise, there is going to be an exception at runtime, which you would need to catch and process.
Related
I am trying to call a generic function where the type of the generic function is determined by an input string.
I want a function that takes in a string, uses reflection to turn that string into a type and then calls a generic function with that type.
I have been working with the solution proposed in How do I use reflection to call a generic method? but I just cannot get it to work.
Any help would be greatly appreciated.
//This is the generic function(just a simple example function that writes out the incoming type):
public class SelectableRepository
{
public virtual string GetTypeOfEntity<TEntity>()
{
return typeof(TEntity).ToString();
}
}
//This is the controller that is attempting to call the generic function with the type of "inputString"
public class MyController
{
public void MyFunction(string inputString)
{
//This is how I have been trying to invoke the method but to no avail:
var myType = Type.GetType("Models.DatabaseModels." + inputString); //Get the type of the input string
MethodInfo method = typeof(SelectableRepository).GetMethod("GetTypeOfEntity"); //Get the generic method from SelectableRepository
MethodInfo genericMethod = method.MakeGenericMethod(myType); //Initialize the generic method with the type from the input string
genericMethod.Invoke(this, null); //Call the method, I'm pretty sure this is wrong
}
}
//This is an example of a class that could go into the generic method, the inputString would then be "Company"
namespace Models.DatabaseModels
public class Company
{
public string Name { get; set; }
}
I put a breakpoint on the genericMethod.Invoke line and looked at the contents of my variables:
myType = {Models.DatabaseModels.Company}
method = {System.String GetTypeOfEntity[TEntity]()}
genericMethod = {System.String GetTypeOfEntity[Company]()}
I am getting an error on the last line of this code: genericMethod.Invoke(this, null);
The error reads: TargetException: Object does not match target type.
Am I doing something completely wrong?
So after implemented Page Object Pattern using this tutorial i have several
Pages that derived from BasePageElementMap.
And i want to handle some operation so i have this class:
public class DownloadAttachmentsHandler
{
public DownloadAttachmentsHandler(BasePageElementMap basePageElementMap)
{
Type type = basePageElementMap.GetType();
}
}
Every Pages that derived from BasePageElementMap have this html elements that locate inside its class that derived from BasePageElementMap and from this Page i have this Map object that contains all my HTML elements that i am using.
public class YahooEmailPage: BasePage<YahooEmailPageElementMap, YahooEmailPageValidator>...
so in case i am call this function like this:
UploadAttachmentsHandler att = new UploadAttachmentsHandler(new YahooEmailPage().Map);
I want to cast this into YahooEmailPage from my DownloadAttachmentsHandler method.
So currently i have this type object, how can i case it into YahooEmailPage ?
If I understood correctly, you want the following:
public class DownloadAttachmentsHandler
{
public static object Cast(object obj, Type t)
{
try
{
var param = Expression.Parameter(obj.GetType());
return Expression.Lambda(Expression.Convert(param, t), param)
.Compile().DynamicInvoke(obj);
}
catch (TargetInvocationException ex)
{
throw ex.InnerException;
}
}
public DownloadAttachmentsHandler(BasePageElementMap basePageElementMap)
{
Type type = basePageElementMap.GetType();
dynamic foo = Cast(basePageElementMap, type);
}
}
Based on this answer by balage.
EDIT: For the example, lets assume that GetType() returns the type bar. You will have to create a method like this one:
public static void UseDynamic(bar input)
{
// Stuff
}
And then do
public DownloadAttachmentsHandler(BasePageElementMap basePageElementMap)
{
Type type = basePageElementMap.GetType();
dynamic foo = Cast(basePageElementMap, type);
UseDynamic(foo);
}
You can use overloads to avoid having to write many ifs or a switch. However, whichever approach you take, you will have to create a method for each possible type.
is it possible to make generic function in c# that get as input some class and method (of the class) and parameters to the method ( and maybe the result type ) and make instance of that class and call to the function of the class with the parameters and return the result?
Sure.
public class MyClass
{
public class Test
{
public int TestMethod(int a, int b)
{
return a + b;
}
}
public static void Main()
{
int result = ExecuteMethod<Test, int>("TestMethod", 1, 2);
Console.Read();
}
public static TResult ExecuteMethod<TClass, TResult>(string methodName, params object[] parameters)
{
// Instantiate the class (requires a default parameterless constructor for the TClass type)
var instance = Activator.CreateInstance<TClass>();
// Gets method to execute
var method = typeof(TClass).GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance);
// Executes and returns result
return (TResult)method.Invoke(instance, parameters);
}
}
Unless Reflection is your absolute option, use one of the following delegates:
Action<T>: Will let you execute a method that does not return a value. There are several overloads that will let you pass in additional arguments.
Func<TResult>: Will let you execute a method that returns a result of type TResult. There are more overloads that will let you pass in additional arguments. They all follow the syntax Func<T1, T2, T3, TResult> and so on.
And finally, you can define your own delegate.
Yes it's possible. You can do that with reflection.
Here you have a few useful links
Create an instance with reflection
How to invoke method with parameters
Here's how you create an instance of a class using reflection and then call a method on that class.
Assuming you have a class:
public class MyType
{
public void DoSomething()
{
// do stuff here
}
}
You could do the following:
Type instanceType = Type.GetType("MyType");
object instance = Activator.CreateInstance(instanceType);
MethodInfo method = instanceType.GetMethod("MethodName");
object returnValue = method.Invoke(instance, new object[] { /* paramaters go here */ });
Is there any way to do something like this?
void SomeMethod(Type generic)
{
SomeGenericMethod<generic>();
}
I need to pass the type in as a "normal" parameter and not a generic type parameter.
You can do it with reflection:
public class foo
{
public void SomeMethod(Type type)
{
var methodInfo = this.GetType().GetMethod("SomeGenericMethod");
var method = methodInfo.MakeGenericMethod(new[] { type });
method.Invoke(this, null);
}
public void SomeGenericMethod<T>()
{
Debug.WriteLine(typeof(T).FullName);
}
}
class Program
{
static void Main(string[] args)
{
var foo = new foo();
foo.SomeMethod(typeof(string));
foo.SomeMethod(typeof(foo));
}
}
That said, using reflection in this way means you are losing some of the benefits of using generics in the first place, so you might want to look at other design alternatives.
Assuming that your method is defined in a class called MyClass, this should do it:
var MyObject = new MyClass();
typeof(MyClass).GetMethod("SomeGenericMethod").MakeGenericMethod(generic).Invoke(myObject, null);
Type.GetMethod() gets an object which describes a method defined in the Type it's called on.
The method is generic, so we need to call MakeGenericMethod, and pass its one generic parameter.
We then invoke the method, passing the object we want the method called on and any arguments it takes. As it takes no arguments, we just pass null.
Why doesn't this work?
namespace InvokeTest
{
public class MethodInvoker
{
public static void Execute<T>(Expression<Action<T>> call)
{
// Parameter count mismatch
call.Compile().DynamicInvoke();
// Also attempted this:
//call.Compile().DynamicInvoke(1);
// but it threw: "Object of type 'System.Int32' cannot be converted to type 'InvokeTest.TestClass'."
}
}
public class TestClass
{
public TestClass()
{ }
public void DoSomething(int num)
{
System.Console.WriteLine("It works");
}
public void KickOff()
{
MethodInvoker.Execute<TestClass>(m => m.DoSomething(1));
}
}
}
An Action<T> is the same as a delegate void F<T>(T t). That is, an Action<T> is an method that has return type void and consumes instances of T.
When you call
MethodInvoker.Execute<TestClass>(m => m.DoSomething(1));
you have set the type parameter T to be TestClass. Therefore, you need to pass in a parameter, and that parameter has to be instance of TestClass.
This is why in the first case you get a parameter count mismatch, and in the second case the compiler wants to convert the parameter to an instance of TestClass. You need to pass one parameter, and that parameter needs to be an instance of TestClass.
Note that your action is
m => m.DoSomething(1).
So your action takes an instance of TestClass which you are parameterizing by m and invokes m.DoSomething(1) on that instance. Now, when you dynamically invoke this method, you need to be giving it an instance of TestClass. You aren't doing that.
Since you defined the parameter type
Expression<Action<T>>
the function will require expression in accordance with the following delegate:
Action<TestClass>
which is:
public void SampleMethod(TestClass a)
Based on this the correct invocation should look like this:
var t = new TestClass();
call.Compile().DynamicInvoke(t);