How to use reflection to call method by name - c#

Hi I am trying to use C# reflection to call a method that is passed a parameter and in return passes back a result. How can I do that? I've tried a couple of things but with no success. I'm used to PHP and Python where this can be done on a single line so this is very confusing to me.
In essence this is how the call would be made without reflection:
response = service.CreateAmbience(request);
request has these objects:
request.UserId = (long)Constants.defaultAmbience["UserId"];
request.Ambience.CountryId = (long[])Constants.defaultAmbience["CountryId"];
request.Ambience.Name.DefaultText = (string)Constants.defaultAmbience["NameDefaultText"];
request.Ambience.Name.LanguageText = GetCultureTextLanguageText((string)Constants.defaultAmbience["NameCulture"], (string)Constants.defaultAmbience["NameText"]);
request.Ambience.Description.DefaultText = (string)Constants.defaultAmbience["DescriptionText"];
request.Ambience.Description.LanguageText = GetCultureTextLanguageText((string)Constants.defaultAmbience["DescriptionCulture"], (string)Constants.defaultAmbience["DescriptionDefaultText"]);
This is my function to implement the reflection where serviceAction for the case above would be "CreateAmbience":
public static R ResponseHelper<T,R>(T request, String serviceAction)
{
ICMSCoreContentService service = new ContentServiceRef.CMSCoreContentServiceClient();
R response = default(R);
response = ???
}

Something along the lines of:
MethodInfo method = service.GetType().GetMethod(serviceAction);
object result = method.Invoke(service, new object[] { request });
return (R) result;
You may well want to add checks at each level though, to make sure the method in question is actually valid, that it has the right parameter types, and that it's got the right return type. This should be enough to get you started though.

Here's a quick example of calling an object method by name using reflection:
Type thisType = <your object>.GetType();
MethodInfo theMethod = thisType.GetMethod(<The Method Name>);
theMethod.Invoke(this, <an object [] of parameters or null>);

If you're on .NET 4, use dynamic:
dynamic dService = service;
var response = dService.CreateAmbience(request);

You can use Delegate.CreateDelegate to obtain a delegate to the method by name:
public static R ResponseHelper<T,R>(T request, string serviceAction)
{
var service = new ContentServiceRef.CMSCoreContentServiceClient();
var func = (Func<T,R>)Delegate.CreateDelegate(typeof(Func<T,R>),
service,
serviceAction);
return func(request);
}

Related

C# - Using reflection with JsonSchema4.FromTypeAsync

[INTRO]
I know there are about a zillion QA about generics and reflections everywhere, but it's becoming a blackhole to me, and I'm only getting more lost the more I read!!
What i need to do is simple, and I'm amazed that it hasn't been addressed before.
[SAMPLE] Consider the following snippit:
public async Task<string> generateJsonSchema(string model)
{
try
{
string modelName = "Models." + model;
Type t = Type.GetType(modelName, false);
JsonSchema4 schema = await JsonSchema4.FromTypeAsync<t>();
return schema.ToJson();
}
catch (Exception ex)
{
Logger.WriteToLogFile(ex.ToString(), "exception");
return "";
}
}
[PROBLEM] Now the main problem is that variable t is evaluated at runtime, thus, JsonSchema4.FromTypeAsync<t>() throws the error 't' is a variable but is used like a type when trying to build compile time
Whoever used JsonSchema4 will understand what I'm trying to achieve here.
Instead of creating a generate function for each of my models, or make a switch/if-else logic,
[QUESTION]
How to make it receive the model name as a string parameter, and convert the string-model-name to model-type and pass it to jSonSchema4 method.
The problem here is that, as you say, t is evaluated as runtime.
I also ran into this Problem and solved it by creating a MethodInfo of the method I wanted to invoke, in your case JsonSchema4.FromTypeAsync<t>().
So basically this is want may fix the problem:
var methodInfo = typeof(JsonSchema4).GetMethod("FromTypeAsync", new Type[] { }); //Get the "normal method info", the overload without parameters
var methodInfoWithType = methodInfo.MakeGenericMethod(t); //now you have a method with your desired parameter t as TypeParameter
Task<JsonSchema4> task = methodInfoWithType.Invoke(null, null) as Task<JsonSchema4>; //now you can cast the result from invoke as Task to keep the Async-Await functionality
var schema = await task;
return schema.ToJson();

How to return a specific type from a generic method?

I am trying to mock a utility generic class but I'm not having success in returning a specific type from a method that uses generics. My implementation below is not working, the compiler is complaining. Can someone help cast and return the specific object I need?
public HttpResponse<TResponse> SubmitRequest<TResponse>(string serviceUri, HttpVerbEnum httpVerb, HttpContentTypeEnum contentType, NameValueCollection header)
{
string RawResponse = "";
HttpResponse<CreateSaleResponse> Response = new HttpResponse<CreateSaleResponse>(new CreateSaleResponse(), RawResponse, System.Net.HttpStatusCode.Created);
return Response;
}
Doing what you're currently doing is probably tossing out a "cannot implicitly convert type" error. If you're going to return one specific type, then that method doesn't need to support generics.
What you seem to want is something like this, that uses the type you specify:
public HttpResponse<TResponse> SubmitRequest<TResponse>(string serviceUri, HttpVerbEnum httpVerb, HttpContentTypeEnum contentType, NameValueCollection header)
{
string RawResponse = "";
HttpResponse<TResponse> Response = new HttpResponse<TResponse>(new TResponse(), RawResponse, System.Net.HttpStatusCode.Created);
return Response;
}
You pass in the type when you call the method:
var response = SubmitRequest<SomeAppropriateType>(...); // pass in your parameter values
As far as how to mock this (for testing purposes?), that depends on the mock library you're using.

"Operation could destabilize the runtime" when calling dynamic method

I am trying to right a Func that I can use to access the get method of a property, but have hit a stumbling block.
The dynamic method below is created fine, however when it is invoked I get the following error.
VerificationException, Operation could destabilize the runtime.
I have checked that the il code emits a valid function by writing it to a class rather than a dynamic method and all would appear to be fine.
I'm guessing it has to do with some typing issue but I'm not sure where, so any help is appreciated.
Example class
public class DemoClass
{
public string Property{get;set;}
}
Dynamic method creation
var getMethods = new DynamicMethod(string.Empty,
typeof(string),
new Type[] {typeof(object) });
var ilGet = getMethods.GetILGenerator();
var falseGetLabel = ilGet.DefineLabel();
ilGet.Emit(OpCodes.Ldarg_1);
ilGet.Emit(OpCodes.Isinst, typeof(DemoClass));
ilGet.Emit(OpCodes.Brfalse_S, falseGetLabel);
ilGet.Emit(OpCodes.Ldarg_1);
ilGet.Emit(OpCodes.Isinst, typeof(DemoClass));
ilGet.Emit(OpCodes.Call, typeof(DemoClass).GetProperty("Property").GetMethod);
ilGet.Emit(OpCodes.Ret);
ilGet.MarkLabel(falseGetLabel);
ilGet.Emit(OpCodes.Newobj,
typeof(InvalidOperationException).GetConstructor(Type.EmptyTypes));
ilGet.Emit(OpCodes.Throw);
var f = (Func<object,string>)getMethods.CreateDelegate(
typeof(Func<object,string>));
var x = new DemoClass{Property = "9"};
Console.WriteLine(f(x)); <--- fails here
You should use OpCodes.Ldarg_0 instead of OpCodes.Ldarg_1 to get first method argument.

A Type created at runtime as a Type Parameter for a Generic Type

I have this senario where I need to provide a type for json deserialization, how do I make it dynamic?
Here the code uses an example class name(rd.ds.SDK.re.sa), but in the real code the type will be constructed using data from database.
Type dynamicalyCreatedAtRuntime = Type.GetType("rd.ds.SDK.re.sa");
var response = webRequest.ReadJsonResponseAs<Result<AnalysisResult<dynamicalyCreatedAtRuntime>>();
public static T ReadJsonResponseAs<T>(this HttpWebRequest webRequest)
{
using (System.IO.Stream responseStream = webRequest.GetResponse().GetResponseStream())
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(responseStream))
{
T result = JsonConvert.DeserializeObject<T>(sr.ReadToEnd());
responseStream.Close();
return result;
}
}
}
Use this overload of JsonConvert.DeserializeObject instead:
public static object ReadJsonResponseAs(this HttpWebRequest webRequest, Type type)
{
using (System.IO.Stream responseStream = webRequest.GetResponse().GetResponseStream())
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(responseStream))
{
object result = JsonConvert.DeserializeObject(sr.ReadToEnd(), type);
responseStream.Close();
return result;
}
}
}
Obviously, you'll need to treat the result as an object. Since you won't know the type of the object during compile-time, there's no point in using generics or using reflection to invoke the generic overload.
You should also create a non generic version of these types: Result<T> and AnalysisResult<T>.
Having to dynamically create types such as Result<AnalysisResult<dynamicalyCreatedAtRuntime>> at runtime could be done with reflection, but there would be 0 benefits in doing so. This screams for a redesign.
MethodInfo.MakeGenericMethod serves this purpose.
If you can, I would use Netwonsoft.JSON (Json.Net) for this.
var json = ...get request body...
var type = Type.GetType("a.b.c");
var obj = JsonConvert.DeserializeObject(json, type);
Lots of help out there for working with Json.NET too. This may not work if you aren't in control of the classes, and they need custom conversions.
If you absolutely must call the Generic method, you are going to need to use reflection to manually construct the call. This gets tricky, and probably isn't worth it.
var m = webRequest.GetType().GetMethod("ReadJsonResponseAs");
var func = m.MakeGenericMethod(type);
func.Invoke(webRequest, null); /* invoke func on webRequest object, no params */
Are your generic types that deeply nested? If so you will probably have to make each level separately.
Use reflection to get the method to start with, then "construct" it by supplying type arguments with MakeGenericMethod:
Type dynamicalyCreatedAtRuntime = Type.GetType("rd.ds.SDK.re.sa");
MethodInfo method = webRequest.GetType().GetMethod("ReadJsonResponseAs");
MethodInfo generic = method.MakeGenericMethod(dynamicalyCreatedAtRuntime );
generic.Invoke(this, null);

How to call an IronPython 2 method from C#

I can get an IronPython 2 class back to my C#. What is the new IronPython 2 way of calling a member on that class?
object ironPythonClass = scope.GetVariable("Hamish");
object[] args = new object[0];
object pythonObject = engine.Operations.Call(ironPythonClass, args);
var member = "Home";
// old way IronPython 1
// var methodResult = Ops.Invoke(this.pythonObject, SymbolTable.StringToId(member), args);
I thought all I'd have to do was
var methodResult = PythonOps.Invoke(codeContext, pythonObject, SymbolTable.StringToId(member), args);
but creating a dummy CodeContext doesn't seem to be right. I feel as though I should be able to derive one from my
code.Execute();
that runs the Python file creating the class, plus the scope that arises out of that execution.
Found a way to do it:
var ops = engine.Operations;
var x = ops.GetMember(pythonObject, member);
var h = ops.Call(x, new object[0]);
Looks like the Operations produces an OperationsObject which has useful members.
Looking at the DLR code (Microsoft.Scripting.Hosting) however I see that Call is deprecated:
[Obsolete("Use Invoke instead")]
public object Call(object obj, params object[] parameters) {
return _ops.Invoke(obj, parameters);
}
My version of scripting 0.9.20209, doesn't yet have the Invoke however.
After updating to the newer IronPython 2.6Beta and its scripting dlls I find I can write:
var h = ops.InvokeMember(pythonObject, member, new object[0]);

Categories