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
I want to call a python function from C# code. To do that, I used Python for .NET to call function as shown in the following lines of code
using System;
using Python.Runtime;
public class Test{
public static void Main(){
using(Py.GIL()){
dynamic lb = Py.Import("lb");
dynamic result = lb.analyze("SomeValue");
Console.WriteLine(result);
}
}
}
The python function is something like this:
def analyze(source, printout = False, raw = True):
# removed for bravity
So the question is, how can I set "raw" to False when I call the analyze function from C# code. I tried the following but it didn't work.
1. dynamic result = lb.analyze("SomeSource", raw : false); // No result
2. dynamic result = lb.analyze("SomeSource", #"raw = False"); // No result
I know it is easy to do by doing this:
dynamic result = lb.analyze("SomeSource", False, False);
But what if there is more than six or seven named parameter, it would not be great to insert it all manually and change the one I wanted. For example, the python library that I am using have 12 named parameter with default value including two more parameters with no default value.
UPDATED
I also tried:
3. dynamic result = lb.analyze("SomeSource", raw = false); // C# compilation error
To apply keyword arguments use:
lb.analyze("SomeSource", Py.kw("raw", false));
See readme.
Another approach is using C# keyword argument syntax that was recently added to pythonnet:
lb.analyze("SomeSource", raw: false);
Since I am using one function to call python scripts I have used a Listto hold the parameter values. I am also passing in a class name and function name since my python scripts contain multiple classes with multiple functions. I don't use the 'self' parameter in any of my classes, so they are static functions. I am providing a snippet of my code to help you and anyone else out that is using python in .net. I personally use it for communciation with USB.
Here is an example of one of my callers. Ignore the function name but look at how it's calls the ExecuteScript and passing int he parameterset. Notice the list is of type object, incase your paramters are a mix of string/int/bool/objects etc.
public string SendCommand(string comport, string command)
{
try
{
List<object> parameterSet = new() { comport, command };
string result = _pythonService.ExecuteScript<string>("usb", "usb", "sendCommand", parameterSet);
return result;
}
catch (Exception)
{
throw;
}
}
Here is a function that executes the class function
public dynamic? ExecuteScript<T>(string scriptFile, string className, string functionName, List<object> paramset)
{
T? result = default;
try
{
// Location of all the python scripts in the project. lets get the python file we are specifying in the function param
string file = $"{Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)}\\PythonScripts\\{scriptFile}.py";
// This is initalized in the program.cs, but just want to make sure it's initialized in case something happens
if (!PythonEngine.IsInitialized)
{
PythonEngine.Initialize();
Py.GIL();
}
using (var scope = Py.CreateScope())
{
PyObject? pythonReturn; // Our returned PythonObject from the python function call
string code = File.ReadAllText(file); // Get the python file as raw text
var scriptCompiled = PythonEngine.Compile(code, file); // Compile the code/file
scope.Execute(scriptCompiled); // Execute the compiled python so we can start calling it.
PyObject pythonClass = scope.Get(className); // Lets get an instance of the class in python
// Add parameters to the function?
if (paramset != null && paramset.Count > 0)
{
PyObject[] pyParams = new PyObject[paramset.Count]; // This is an array of python parameters passed into a function
// Loop through our incoming c# list of parameters and create PythonObject array .
for (int i = 0; i < paramset.Count; i++)
{
pyParams[i] = paramset[i].ToPython();
}
pythonReturn = pythonClass.InvokeMethod(functionName, pyParams); // Call the function on the class with parameters
}
else // We aren't using parameters here
pythonReturn = pythonClass.InvokeMethod(functionName); // Call the function on the class
// Lets convert our returned pythonObject to that of the object type (C#)
object? netObject = pythonReturn.AsManagedObject(typeof(object));
// A special case of when we want a list back. We will convert the object to the specific type in the caller function
if (typeof(T) == typeof(IList<object>))
{
object[] something = pythonReturn.As<object[]>();
return something;
}
// Convert the c# object to that of what we expect to be returned,. string/int/bool/class
if (netObject != null)
result = (T)netObject; // convert the returned string to managed string object
}
return result;
}
catch (Exception)
{
// Handle your exceptions here
throw;
}
}
If you don't care about the entire function and just want the quick snippet of adding the params:
// Add parameters to the function?
if (paramset != null && paramset.Count > 0)
{
PyObject[] pyParams = new PyObject[paramset.Count]; // This is an array of python parameters passed into a function
// Loop through our incoming c# list of parameters and create PythonObject array .
for (int i = 0; i < paramset.Count; i++)
{
pyParams[i] = paramset[i].ToPython();
}
pythonReturn = pythonClass.InvokeMethod(functionName, pyParams); // Call the function on the class with parameters
}
else // We aren't using parameters here
pythonReturn = pythonClass.InvokeMethod(functionName); // Call the function on the class
I imported the taglib-sharp dll (that had been copied to the bin/debug folder of my project) in my C# application and then used types and methods from the library in the following way:
using TagLib;
private void method()
{
TagLib.File file = TagLib.File.Create("C:\\temp\\some.mp3");
TagLib.Tag tag = file.GetTag(TagLib.TagTypes.Id3v2);
}
Now I want to link the dll dynamically. How can I implement the same functional in this case?
That, what I've tried:
using System.Reflection
private void method()
{
Assembly TagLib = Assembly.Load("taglib-sharp");
Type TagLibFile = TagLib.GetType("File");
dynamic LibFile = Activator.CreateInstance(TagLibFile);
TagLibFile file = LibFile.Create("c:\\temp\\some.mp3");
}
In this implementation, VisualStudio says that I can't use the tagLibFile variable as a type. I supposed that when I get a type from dll, I will be able to create variables of this type.
By the way, is this approach is correct?
P.S. Also, I tried to use the invoke method, but I was not sure what object I should pass as a first argument.
UPD
Based on #nawfal's awnser below, I've got the following working code:
using System.Reflection
private void method()
{
Assembly TagLib = Assembly.Load("taglib-sharp");
// get the File type
var fileType = TagLib.GetType("TagLib.File");
// get the overloaded File.Create method
var createMethod = fileType.GetMethod("Create", new[] { typeof(string) });
// get the TagTypes method that contains Id3v2 field
Type tagTypes = TagLib.GetType("TagLib.TagTypes");
// get the overloaded File.GetTag method
var getTagMethod = fileType.GetMethod("GetTag", new[] {tagTypes});
// obtain the file
dynamic file = createMethod.Invoke(null, new[] { "C:\\temp\\some.mp3" });
// obtain the Id3v2 field value
FieldInfo Id3TagField = tagTypes.GetField("Id3v2");
var Id3Tag = Id3TagField.GetValue(tagTypes);
// obtain the actual tag of the file
var tag = getTagMethod.Invoke(file, new[] { Id3Tag });
}
You should be doing something like this:
private void method()
{
var assembly = Assembly.Load("taglib");
var type = assembly.GetType("namespace.File"); // namespace qualified class name
// assuming you only have one Create method, otherwise use reflection to resolve overloads
var method = type.GetMethod("Create");
dynamic file = method.Invoke(null, new[] { "C:\\temp\\some.mp3" }); // null for static methods
var tag = file.GetTag(TagLib.TagTypes.Id3v2); // not sure if you can pass those params,
// may be do reflection to get them too
}
Kindly rethink if you want it to be dynamic. If you can reference the dll then you can still get the benefits of strong typing.
Save it as object.
object file = LibFile.Create(fi.FullName);
Should work.
Dynamic loading dlls works much different.
I am trying to load an assembly, System.Speech, via reflection, so that I can use the SpeakAsync method to read aloud some text.
I wrote this:
System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom("System.Speech.dll");
System.Type type = assembly.GetType("System.Speech.SpeechSynthesizer");
var methodinfo = type.GetMethod("SpeakAsync", new System.Type[] {typeof(string)} );
if (methodinfo == null) throw new System.Exception("No methodinfo.");
object[] speechparameters = new object[1];
speechparameters[0] = GetVerbatim(text); // returns something like "+100"
var o = System.Activator.CreateInstance(type);
methodinfo.Invoke(o, speechparameters);
But get the error
System.NullReferenceException: Object reference not set to an instance of an object
Your code contains bug, you can't work with class if you specified incorrect namespace (neither via reflection nor without it)
You use incorrect namespace here (that's why you received null reference exception ):
System.Type type = assembly.GetType("System.Speech.SpeechSynthesizer");//type == null
Here is example of correct namespaces:
System.Type type = assembly.GetType("System.Speech.Synthesis.SpeechSynthesizer");
Update1:
Another note. invoke returns a prompt, and you shouldn't exit the programm while asynchronous method is working (off course, only if you really want to listen speech to the end). I added few lines to your code to wait until speach will be finished:
internal class Program
{
private static void Main(string[] args)
{
var assembly = Assembly.LoadFrom("System.Speech.dll");
var type = assembly.GetType("System.Speech.Synthesis.SpeechSynthesizer");
var methodinfo = type.GetMethod("SpeakAsync", new[] {typeof(string)});
if (methodinfo == null) throw new Exception("No methodinfo.");
var speechparameters = new object[1];
speechparameters[0] = "+100"; // returns something like "+100"
var o = Activator.CreateInstance(type);
var prompt = (Prompt) methodinfo.Invoke(o, speechparameters);
while (!prompt.IsCompleted)
{
Task.Delay(500).Wait();
}
}
}
Update 2
Make sure you have the correct language pack.
MSDN
Update 3
If you use Mono, try to make sure that this feature should works on Mono. I gues there are some problems with Mono realization.
given the following classes:
class SomeBuilder<T>
{
public static object Build(int index)
{
...
}
}
class SomeHelper
{
public object GetBuildObj(object value)
{
var valuetype = value.GetType();
var methodinfo = typeof(SomeBuilder<>).MakeGenericType(valuetype).GetMethod("Build");
var handler = SomeDynamicHelper.GetMethodInvoker(methodinfo);
var result = hander(null, new object[]{1});
}
}
SomeBuilder was a generic type so i need a call to MakeGenericType() to make things right.
when i pass a normal type like 'class person' for the value, everything just works, that`s fine.
but when i pass a anonymous type like: new { id=1 }, the handler was successfully created. but invoke this dynamic handler i got a MethodAccessException with these messages:
"method "SomeDynamicHelper.(System.Object, System.Objec[])" try to access method "SomeBuilder'1<<>f__AnonymousType0'1<System.Int32>>.Build(int)" failed.
any help would be appreciated, thx.
btw, if you are interested in SomeDynamicHelper, plz see:
http://www.codeproject.com/Articles/14593/A-General-Fast-Method-Invoker
edit1:
i did the call in main like this:
static void Main(string[] args)
{
// pass a normal class, this will be fine
var value = new Person { id = 1};
new SomeHelper().GetBuildObj(value);
// pass a anonymous type
var value = new { id = 1};
new SomeHelper().GetBuildObj(value); // oops, got a exception here!
}
edit2:
based on the comment i changed my code:
class SomeHelper
{
public object GetBuildObj(object value)
{
//this time i do not use the value, but create a new inner value:
var valuenew = new { id = 1 };
var valuetype = valuenew.GetType();
var methodinfo = typeof(SomeBuilder<>).MakeGenericType(valuetype).GetMethod("Build");
var handler = SomeDynamicHelper.GetMethodInvoker(methodinfo);
var result = hander(null, new object[]{1});
}
}
well, this time there is no exception, but...unfortunately a new problem occured...may be i should open a new thread for the new problem.
thank you guys, for your attentions.
edit3:
hi, after some digging, i also found some useful information. say, the SomeDynamicHelper.GetMethodInvoker() code as below:
DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) }, methodInfo.DeclaringType.Module);
this is the core we used here to dynamic create a method. for our context, we need to declare the anonymous type in same assembly with the SomeHelper and SomeBuilder. but, if we can`t do the declaration, what should we do?
ok, you can call DynamicMethod() with last parameter(the skipVisibility), set to true!
hope this will help others with the same problem :)
"method "SomeDynamicHelper.(System.Object, System.Objec[])" try to access method "SomeBuilder'1<<>f__AnonymousType0'1>.Build(int)"
From this you can see that dynamic method try to run an internal\private method called Build, for that you got MethodAccessException.
(The anonymous type is kept in a new generated class)
Adding InternalVisibleTo not always helping because it's helping only if you can rewrite the anonymous type assembly (manually or with interception) and only if the type and method are internals and not private.
In dynamic method you can bind the method to type\module, and to skip visibility check, but this help to access private members of the specified module, so again if the type you trying to access is in a different assembly and the type\method are private you can't do anything.
Well almost. There is a "secret" attribute that you inject to your assembly, called IgnoreAccessChecksTo and then in addition to bounded module and skip visibility maybe it will work.
You can try using [assembly: InternalsVisibleTo("Anonymously Hosted DynamicMethods Assembly")] in the same project where you define the anonymous type.
I had a similar error with a C# class I tried to inject using IKVM-converted Guice. The fix was simply making the affected class (in this case probably SomeBuilder) public and everything worked fine.
I have problem that after creating object "oListType01" of type List < MyClass01 > and after assigning it to the another objet "oObjectType " of type "object" I can not access any more function "ElementAt(1)". I tried by using reflection but I am always getting exception(parameter conflict) in "Invoke" method. Does anyone knows why ?
Milan
MyClass01 oMy1 = new MyClass01();
oMy1._ID = "1";
MyClass01 oMy2 = new MyClass01();
oMy2._ID = "3";
IList<MyClass01> oListType01 = new List<MyClass01>();
oListType01.Add(oMy1);
oListType01.Add(oMy2);
object oObjectType = new object();
oObjectType = oListType01;
From here fowrads only object oObjectType is available (upwards happens in separate function call in the real case). In VS oObjectType shows two element which I would like to access per reflection.
MethodInfo mInfo = typeof(System.Linq.Enumerable).GetMethod("ElementAt").MakeGenericMethod(typeof(object));
object oSingleObject = mInfo.Invoke(oObjectType, new object[] { 1 });
I will assume you have a valid reason to be doing this but it seems a little wrong. That said here is some code that will accomplish what you are trying to do.
MethodInfo mInfo = typeof(System.Linq.Enumerable).GetMethod("ElementAt").MakeGenericMethod(typeof(MyClass01));
object oSingleObject = mInfo.Invoke(oObjectType, new object[] { oObjectType, 1 });
When I run this code I get the second element in the List.
The ElementAt extension method is probably on IEnumerable<T> and so when you treat your list like an object, the extension method won't be available unless you cast it. Either ((List<MyClass01>)oObjectType).ElementAt() or (oObjectType as List<MyClass01>).ElementAt().
I have to ask, though, with all due respect why you'd ever want to do this in the first place? It strikes me that there's something wrong here that could be done a little cleaner using interfaces.
If we can safely assume that:
oObjectType is a IEnumerable of some T
then here's the code to extract the items from it.
Note that I seriously wonder if this is the right way to go about this, but you haven't given us enough information to help you figure out if there's a better way, so all we're left with is just answering the question as asked.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Diagnostics;
namespace ConsoleApplication2
{
class MyClass01
{
public String _ID;
public override string ToString()
{
return _ID;
}
}
class Program
{
static void Main(string[] args)
{
MyClass01 oMy1 = new MyClass01();
oMy1._ID = "1";
MyClass01 oMy2 = new MyClass01();
oMy2._ID = "3";
IList<MyClass01> oListType01 = new List<MyClass01>();
oListType01.Add(oMy1);
oListType01.Add(oMy2);
object oObjectType = new object();
oObjectType = oListType01;
Test(oObjectType);
Console.In.ReadLine();
}
private static void Test(object oObjectType)
{
Type tObject = oObjectType.GetType();
Debug.Assert(tObject.IsGenericType);
Debug.Assert(tObject.GetGenericArguments().Length == 1);
Type t = tObject.GetGenericArguments()[0];
Type tIEnumerable = typeof(IEnumerable<>).MakeGenericType(t);
Debug.Assert(tIEnumerable.IsAssignableFrom(tObject));
MethodInfo mElementAt =
typeof(Enumerable)
.GetMethod("ElementAt")
.MakeGenericMethod(t);
Console.Out.WriteLine("o[0] = " +
mElementAt.Invoke(null, new Object[] { oObjectType, 0 }));
Console.Out.WriteLine("o[1] = " +
mElementAt.Invoke(null, new Object[] { oObjectType, 1 }));
}
}
}
This is really similar to your other question, but in this case, the static ElementAt method is actually requiring two parameters. Try this:
object oSingleObject = mInfo.Invoke(null, new object[] { oObjectType, 1 });