C# Determine Generic Type Parameter at Runtime - c#

If a named class exists, I want to pass that class as the type parameter to a generic method. Otherwise I want to pass a different type. I cannot figure out how to pass the type parameter to the generic method.
// Does this type exist?
Type objType = Type.GetType(typeof(ModelFactory).Name + "." + content_type + "Model");
// if not, use this type instead
if (objType == null)
{
objType = typeof(GenericModel);
}
// what to pass as the generic type argument?
var other = query.Find<objType>().ContinueWith((t) =>
Is it possible? What do I pass to Find in the last line instead of objType?
Thanks & regards,
-John

You must use the Reflection API. Once you get the argument type for your Find method, you need to get a MethodInfo from the Find method and pass an instance of the class that defines that method and the necessary parameters for the method, an example:
public class GenericModel {}
// This class simulates the class that contains the generic Find method
public class Query {
public void Find<T>() {
Console.WriteLine("Invoking Find method...");
}
}
class Program {
static async Task Main(string[] args) {
var theType = typeof(GenericModel);
// Obtaining a MethodInfo from the Find method
var method = typeof(Query).GetMethod(nameof(Query.Find)).MakeGenericMethod(theType);
var instanceOfQuery = Activator.CreateInstance(typeof(Query));
var response = method.Invoke(instanceOfQuery, null); // Cast the method to your return type of find.
Console.ReadLine();
}
}

Related

Getting the concrete class of a method called inside a delegate in C#

My situation is as follows - a delegate of the form
() => _instanceOfMyClassWhichImplementsInterfaceIX.MyMethod()
is passed as a parameter to a certain method. Inside that method I need to get both the name of the method called in the delegate (in this case "MyMethod") and the actual class of the object (in this case "MyClass") _MyInstanceOfClassWhichImplementsInterfaceIX . Using the method described by user Stipo in his answer to Find the name of the invoked method of a Func delegate I was able to get the name of the method and the name of the interface ("IX" in case), but not the name of the concrete class of the object. Its to done by calling the method "GetCalledMethods" in his code, which returns a liste of MethodBase and calling methodBaseInstance.ReflectedType.Name on it.
How can i do it?
You are better off making the method accept an expression, it will make it easier to gather the info you need. Here is a way you could do that, the interface can be gotten just as easy as here I am getting the actual MethodInfo class, and the actual type it belongs to.
void Main()
{
var _instanceOfMyClassWhichImplementsInterfaceIX = new MyClass();
Expression<Func<bool>> y = () => _instanceOfMyClassWhichImplementsInterfaceIX.MyMethod();
var methodInfo = ((MethodCallExpression)y.Body).Method;
var methodName = methodInfo.Name;
var methodDeclaringType = methodInfo.DeclaringType;
Console.WriteLine(methodName);
Console.WriteLine(methodDeclaringType);
Console.WriteLine(y.Compile().Invoke());
Console.WriteLine(methodDeclaringType.GetInterface(nameof(IMyClass)));
}
public class MyClass : IMyClass
{
public bool MyMethod() => true;
}
public interface IMyClass { }
EDIT: Added to better answer the users question
// If you want to make the expression while referencing as a interface it will stick
// to the expression, meaning we cant get the underlying type from it.
// The easy way around this is trivial just pass the underlying type as a generic with a constraint.
// If you MUST not pass in type T you can do this same method,
// but it cant be inferred when using the method is all.
public void MethodDoThis<T>(IMyClass myclass)
where T : IMyClass
{
Expression<Func<bool>> y = () => myclass.MyMethod<MyClass>();
var methodInfo = ((MethodCallExpression)y.Body).Method;
var methodName = methodInfo.Name;
var methodDeclaringType = methodInfo.DeclaringType;
Console.WriteLine("Method Name: " + methodName);
Console.WriteLine("Method Declaring Type: " + typeof(T));
Console.WriteLine("Method Return Value: " + y.Compile().Invoke());
Console.WriteLine("Method Defined Interface: " + typeof(T).GetInterface(nameof(IMyClass)));
}

How to create an instance of generic type whose constructor requires a delegate function parameter?

I need to use the following generic class and method ParseFrom() in it:
public sealed class MessageParser<T> : MessageParser where T : IMessage<T>
{
public MessageParser(Func<T> factory); //constructor
public T ParseFrom(byte[] data);
}
Now, I do not know the type of the parameter for this class at compile time, so I use type reflection and MakeGenericType() method to do that:
//Assuming itemInstance is given as input parameter
Type typeArgument = itemInstance.GetType();
Type genericClass = typeof(MessageParser<>);
var genericType = genericClass.MakeGenericType(typeArgument);
var instance = Activator.CreateInstance(genericType);
It gives me a runtime error: MessageParser<> does not have a parameterless constructor. But when I try to pass Func<T> factory as a parameter for CreateInstance():
var instance = Activator.CreateInstance(genericType, () => Activator.CreateInstance(typeArgument));
it gives me a compile error: Cannot convert lambda expression to type 'string' because it is not a delegate type. Am I using the wrong syntax for a delegate function here?
Constructing a delegate of an unknown type dynamically isn't as easy as using reflection to call a method, so the easiest option is to just write a statically typed method to construct the delegate, and then just call it using reflection.
public class DelegateCreator
{
public static Func<T> MakeConstructorStatically<T>()
{
return Activator.CreateInstance<T>;
}
public static object MakeConstructorDynamically(Type type)
{
return typeof(DelegateCreator)
.GetMethod(nameof(MakeConstructorStatically))
.MakeGenericMethod(type)
.Invoke(null, Array.Empty<object>());
}
}
To create the Func<T> through reflection, CreateDelegate is the way to go. Therefore a method, that has the expected signature - including the type contraints (T is IMessage<T>)- is needed.
Here's how you can get it work.
A downside is, that you will still need to use reflection to invoke the parser's methods, at least those that work with the type parameter:
public class CreateParserLateBound {
//The method with the matching signature
public static T MessageParserFactory<T>()
where T : IMessage<T>
{
//your factory code, you pass to MessageParser(Func<T> factory) goes here...
return default(T);
}
...
// itemInstance == item that is IMesage<T>, with T unknown at compiletime;
var itemType = itemInstance.GetType();
var boundParserType = typeof(MessageParser<>).MakeGenericType(itemType);
var boundFuncType = typeof(Func<>).MakeGenericType(itemType);
var factoryMethodInstance = typeof(CreateParserLateBound )
.GetMethod("MessageParserFactory")
.MakeGenericMethod(itemType)
.CreateDelegate(boundFuncType);
var parserInstance = Activator.CreateInstance(boundParserType,
new object[]{ factoryMethodInstance } );
//Invoke ParseFrom (also through reflection)
byte[] data = {1,2,3,4};
boundParserType.InvokeMember("ParseFrom",
BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null,
parserInstance, new object[] {data});
Full runnable code # https://dotnetfiddle.net/RIOEXA
The easy answer is to write your own generic method, then call that via reflection.
public static class Foo
{
public static MessageParser<T> CreateParser<T>() where T : IMessage<T>, new()
=> new MessageParser<T>(() => new T());
private static MethodInfo _createMethod = typeof(Foo)
.GetMethods()
.Where(m => m.Name == nameof(CreateParser) && m.IsGenericMethod)
.Single();
public static MessageParser CreateParser(Type type)
=> (MessageParser)_createMethod.MakeGenericMethod(type)
.Invoke(null, new object[] { });
}

Calling a static method of a class using reflection

I'm attempting to call a static class method via reflection and get its return value as follows:
private SA GetData()
{
Type type = Type.GetType("SA010");
Object obj = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("GetSA");
return (SA)methodInfo.Invoke(obj, null);
}
Here's the class and method I'm calling:
public class SA010
{
public static SA GetSA()
{
//do stuff
return SA.
}
}
The problem is i receive a null reference exception on the 'type' variable. GetData() and SA010.GetSA() are in the same namespace.
Any ideas why i might get this error, something to do with it being static maybe?
Your main problem is you need to specify the full namespace of SA010 when using GetType.
Type type = Type.GetType("SomeNamespace.SA010");
However if you are not dynamicly generating the name a easier solution is use typeof, this does not require you to entire the namespace in if the type is already within scope.
Type type = typeof(SA010);
2nd issue you will run in to once you fix the type, if a method is static you don't create a instance of it, you just pass null in for the instance for the Invoke call.
private SA GetData()
{
Type type = typeof(SA010);
MethodInfo methodInfo = type.GetMethod("GetSA");
return (SA)methodInfo.Invoke(null, null);
}
Try to use
Type type = typeof(SA010);
instead of
Type type = Type.GetType("SA010");
it worked for me

How to get and use class/entity type by reflection?

I have these entities that are being called at runtime and I need to be able to return an IQueryable<EntityObject> based on the entity type being called that particular time by string. Let's say the entity is types of food and the class name is Food, so...
return Repository.Read<Food>(); //this is what I am trying to accomplish
However, I don't know that it is Food until runtime and is such only given as a string, so I use reflection:
Type t = Type.GetType(lookupEntityName); //where lookupEntityName = "Food"
How can I use this Type t to replace 'Food' in the original line of code from above:
return Repository.Read<HERE>(); // use 't' to repalce "HERE"
Supposing that your method only contains a return statement (because you have only posted that), you could do something like this (warning: this wasn't tested):
public static IQueryable<T> Read<T>()
{
return Repository.Read<T>();
}
And you could use it like this:
IQueryable<Food> food = Read<Food>();
You have to use the MethodInfo.MakeGenericMethod Method to create your generic method at runtime.
var openMethod = typeof(Repository).GetMethod(nameof(Repository.Read), ...);
var closedMethod = openMethod.MakeGenericMethod(t);
return closedMethod.Invoke(...);
If you need to call a generic method, you must get the MethodInfo of that function and create a generic MethodInfo for the appropiate type.
This is a helper function to do this:
public MethodInfo GetGenericMethod(string MethodName, Type SourceType, Type TargetType)
{
var mInfo = SourceType.GetMethod(MethodName);
return mInfo.MakeGenericMethod(TargetType);
}
And now you can do this:
Type t = Type.GetType(lookupEntityName);
Type tSrc = typeof(Repository);
var result = GetGenericMethod("Read", tSrc, t).Invoke(Repository);
If Read is an static method then pass null to the invoke.

Returning System.Action from static method on generic type with types defined at runtime

Below is a very paired down example of what I am trying to achieve
public class CoolClass<T>
{
public static void DoSomethingCool()
{
// Insert cool generic stuff here.
List<T> coolList = new List<T>();
}
}
public class OtherClass
{
public Action ReturnActionBasedOnStaticGenericMethodForType(Type type)
{
// Ideally this would just be
return CoolClass<type **insert magic**>.DoSomethingCool
}
}
I know if the type is known I can do following and it will return System.Action
return CoolClass<string>.DoSomethingCool
I know if all I wanted to do was invoke the method I can do
Type baseType = typeof(CoolClass<>);
Type[] typeArguments = new [] {type}
Type constructedType = baseType.MakeGenericType(typeArguments);
MethodInfo method = constructedType.GetMethod("DoSomethingCool");
method.Invoke(null,null);
I maybe down the wrong path all together. It seems like I am trying go get method to be a reference to DoSomethingCool method. I am wishing for something like (Action) method.
You're nearly there - you just need Delegate.CreateDelegate:
Action action = (Action) Delegate.CreateDelegate(typeof(action), null, method);

Categories

Resources