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

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);

Related

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[] { });
}

C# Determine Generic Type Parameter at Runtime

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();
}
}

How to call this function with reflection method,Parameters are delegates and generics

// this is code
public class Foo1Parent
{
}
public delegate void CallBack<T>(T arg1);
public delegate void CallBack();
public class Foo
{
public void openWindow<T>(CallBack<T> onWindowAwake) where T:Foo1Parent
{
Debug.Log("test");
onWindowAwake(T);
}
//I use reflection to call methods "openWindow"
public void openCWindow(CallBack onback, string dialogName)
{
Type t = Assembly.GetExecutingAssembly().GetType(dialogName);
MethodInfo meth1 = this.GetType().GetMethod("openWindow");
object[] obj = new object[] { null }
meth.Invoke(this, obj );
}
}
//
Blockquote
The parameters of a generic is CallBack onWindowAwake I don't know how to do it
It is unclear what the question here is but, from the looks of things you are asking:
How do I invoke the generic method openWindow using reflection?
If this is the question then what you need to do after you get the MethodInfo which has the generic definition of openWindow is to create a generic method using MakeGenericMethod
Example:
MethodInfo genericDefinition = this.GetType().GetMethod("openWindow");
MethodInfo genericMethod = genericDefinition.MakeGenericMethod(typeof(Foo1Parent));
object[] obj = new object[] { null };
genericMethod.Invoke(this, obj);
This is the only way you can invoke a generic method, it is not possible to invoke a definition because, it doesn't know what T is.

How do I create a delegate for a property getter from an open generic type

I have a PropertyInfo for a property on an open generic type, obtained in the following way:
public interface ITest<T>
{
T Item { get; }
}
var propertyInfo = typeof(ITest<>).GetProperty("Item");
I'd like to be able to create a concrete, callable delegate, filling in the class type parameter (e.g. Func<ITest<int>, int>) from propertyInfo.
The obvious thing to try is propertyInfo.GetMethod.MakeGenericMethod(typeof(int)).CreateDelegate(...), but this fails because GetMethod isn't generic - it's a property on a generic class.
I know you can get a delegate for this property by applying the type parameter to the type earlier on (e.g. typeof(ITest<int>).GetProperty("Item").GetMethod.CreateDelegate(...)), but I was hoping to only have to look for the property in ITest once, given that it'll be the same search repeated for each type parameter otherwise.
Is there a way to create this delegate, or can it only come about by using typeof(ITest<int>) to start with?
Short Version
Can TestMethod below be made to pass by some implementation of CreateGetter (assuming T::Equals is implemented sensibly)?
public void TestMethod<T>(ITest<T> x)
{
var propertyInfo = typeof(ITest<>).GetProperty("Item");
var getter = CreateGetter<int>(propertyInfo);
Assert(getter(x).Equals(x.Item));
}
You need to instantiate the generic type first and then you can create the delegate from the MethodInfo in the usual way.
var target = typeof(ITest<>).MakeGenericType(typeof(int));
var prop = target.GetProperty("Item");
var dlg = prop.GetMethod.CreateDelegate(typeof(Func<,>).MakeGenericType(target, prop.PropertyType));
If you actually want to use a PropertyInfo, this Method will work:
public static Func<Test<T>, T> CreateGetter<T>(PropertyInfo info)
{
Type type = info.ReflectedType;
PropertyInfo genericProperty = type.MakeGenericType(typeof(T)).GetProperty(info.Name);
return (source) => (T)genericProperty.GetValue(source);
}
Otherwise i would suggest this:
public static Func<Test<T>, T> GetGenericPropertyDelegate<T>(string propertyName)
{
PropertyInfo genericProperty = typeof(Test<>).MakeGenericType(typeof(T)).GetProperty(propertyName);
return (source) => (T)genericProperty.GetValue(source);
}
usage:
public void Test<T>(ITest<T> x)
{
var getter = GetGenericPropertyDelegate<int>("Item");
Assert(getter(x).Equals(x.Item));
}

How can get the type of nested generic type

There is a third-party component library like this:
public static class GenericExcuteTestClassExtension
{
public static void Excute<T>(this GenericExcuteTestClass clazz,
string parameter, Action<ReturnClass<T>> callback)
{
ReturnClass<T> rClazz = new ReturnClass<T>(parameter);
rClazz.Property5 = typeof(T).ToString();
callback.Invoke(rClazz);
}
public static void Excute<T>(this GenericExcuteTestClass clazz,
string parameter, Action<ReturnClass> callback)
{
ReturnClass rClazz = new ReturnClass(parameter);
rClazz.Property5 = typeof(T).ToString();
callback.Invoke(rClazz);
}
}
I want to reflect to invoke the methodExcute<T>(this GenericExcuteTestClass clazz, string parameter, Action<ReturnClass<T>> callback).
I use thetypeof(GenericExcuteTestClassExtension).GetMethod("Excute", new Type[] { typeof(GenericExcuteTestClass), typeof(string), typeof(Action<ReturnClass<>>)}), but the compiler get the error "Type expected". How can I get type of (Action<ReturnClass<>>), Action<> can compliled, but it's not my expected.
I want to pass the a custom action<ReturnClass<>> like (result)=>{....} to the method, how can I do it?
Please help ,thanks.
Why I use reflect to execute this ?
Because this method must execute in aop intercept
this real situation is like this:
I want to use restsharp in my app, and I write an interface like
[RestfulService(Constants.BASE_URL + "/login")]
public interface UserService
{
[Request("/login")]
void Login([Paramter] string name, [Paramter] string password, Action<T> callBack);
}
and Intercept the interface to get the parameter to execute restsharp ExecuteAsync<T>(this IRestClient client, IRestRequest request, Action<IRestResponse<T>> callback) to get data.
So I need to pass the T in UserService to ExecuteAsync<T> in Intercept method public void Intercept(IInvocation invocation) of castle.windsor, in this method body, we only can get GenericType's Type cannnot get T, so if I directly call ExecuteAsync, I cannot pass the GenericType T to this method. I must use like this:...GetMethod("...").MakeGenericType(new Type[]{piLast.ParameterType.GenericTypeArguments})
The whole problem comes from the fact, that nested generic types are not well handled by the reflection system in .NET.
The simplest solution in your case is to filter the methods yourself. A quick&dirty snippet:
MethodInfo method = null;
foreach (var m in typeof(GenericExcuteTestClassExtension)
.GetMethods(BindingFlags.Public | BindingFlags.Static))
{
var parameters = m.GetParameters();
if (!parameters.Any())
continue;
var lastParameterType = parameters.Last().ParameterType;
var genericArgument = lastParameterType
.GetGenericArguments()
.SingleOrDefault();
// you can/should add more checks, using the Name for example
if (genericArgument != null && genericArgument.IsGenericType)
{
method = m;
break;
}
}
You should probably make an utility method from this. A general approach allowing to search for any method with nested generics can be found here. There is also another possibility using Expressions here.

Categories

Resources