I have a minor but rather annoying problem.
I am doing some tests using a PrivateObject to access various methods in a class. This all works fine. However when the method signature contains "ref" the ref keyword does not seem to have any effect.
private bool NewDeviceArrivedDeviceAtWorkcenter(ThreadStartArgs args, ref Device deviceAtStation)
{
//..SomeCode
deviceAtStation = null;
//...Method to test
}
This test is failing..
[TestMethod]
public void CheckForDeviceAtWorkcenterNoDeviceFound()
{
Initialization omitted
var device = new Device();
var result = accessor.Invoke("NewDeviceArrivedDeviceAtWorkcenter",
new []
{
typeof (ThreadStartArgs),
typeof (Device).MakeByRefType()
},
new object[]
{
threadStartArgs,
device
});
Assert.IsNull(device);
}
Question: Why is device obj in the test method not set to null?
Any help appreciated
Kind Regards
Carsten
The return is made through the argument array passed into the Invoke.
[TestMethod]
public void CheckForDeviceAtWorkcenterNoDeviceFound()
{
//Initialization omitted for publicObject, threadStartArgs, device
Type[] myTypes = new Type[] {typeof (ThreadStartArgs),
typeof (Device).MakeByRefType() };
object[] myArgs = new object[] { threadStartArgs, device };
string sMethod = "NewDeviceArrivedDeviceAtWorkcenter";
//Invoke method under test
bool bResult = (bool)publicObject.Invoke(sMethod, myTypes, myArgs);
Device returnDevice = (Device)myArgs[1];
Assert.IsNull(returnDevice);
}
According to this reply you should get the MethodInfo of the method you want to test and invoke it just with the parameters array.
Have you tried invoking the method just with typeof(Device), without the MakeByRefType() call?
Related
I'm currently working on an compiler in C#, where the behaviour is defined by LambdaExpressions, and then using CompileToMethod, transformed into MethodBuilders and saved to DLL. All functions are public and static.
However, I could not find a way to extract usable MethodInfo (or another method of reference) from the MethodBuilder until the behaviour is defined and declaring type is created/sealed. That means that at that until that point, it is impossible to use Expression.Call to call these functions. That makes self-recursion or mutual referencing between two functions impossible.
I ended up using Reflection to invoke the functions at runtime, but it's very suboptimal, and I'm still curious if there's a better way.
How do i ensure functions created with LambdaExpression.CompileToMethod(MethodBuilder) can self-call?
Alternatively, is there any other way to use LambdaExpressions which would allow this and support saving as a static method to a dll?
I hope this helps.
This is complete code example which produces runtime defined type with single static recursive method.
For the simplicity of the example the recursive method is infinite - at the end of the Main method the recursive method is called
static void Main(string[] args)
{
var moduleBuilder = CreateDynamicModuleBuilder();
var typeBuilder = moduleBuilder.DefineType("Person", TypeAttributes.Public | TypeAttributes.Class);
var methodBuilder = typeBuilder.DefineMethod("SayHello", MethodAttributes.Static | MethodAttributes.Public);
var methodExpression = CreateRecursiveExpression();
var lambda = Expression.Lambda(methodExpression);
lambda.CompileToMethod(methodBuilder);
var typeInfo = typeBuilder.CreateType();
var methodInfo = typeInfo.GetMethod("SayHello", BindingFlags.Public | BindingFlags.Static);
methodInfo.Invoke(null, null);
}
private static Expression CreateRecursiveExpression()
{
var methodInfo = typeof(Console).GetMethod("WriteLine", new[] { typeof(String) });
var arg = Expression.Constant("Hello");
var consoleCall = Expression.Call(methodInfo, arg);
var sayHelloActionVariable = Expression.Variable(typeof(Action), "sayHelloAction");
var block = Expression.Block(
new[] { sayHelloActionVariable },
Expression.Assign(
sayHelloActionVariable,
Expression.Lambda(
Expression.Block(
consoleCall,
Expression.Invoke(sayHelloActionVariable)
)
)
),
Expression.Invoke(sayHelloActionVariable)
);
return block;
}
private static ModuleBuilder CreateDynamicModuleBuilder()
{
var name = new AssemblyName("Example.DynamicRecursion");
var am = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
var mb = am.DefineDynamicModule(name.Name, $"{name.Name}.dll");
return mb;
}
This code will create type with the following signature
public class Person
{
public static void SayHello()
{
Action sayHelloAction;
sayHelloAction = () =>
{
Console.WriteLine("Hello");
sayHelloAction();
}
sayHelloAction();
}
}
I want to make a machine learning api for use with a web application, the field names will be passed to the api with their data types.
Currently I am making a class at runtime with the code provided in this answer: https://stackoverflow.com/a/3862241
The problem arises when I need to call the ML.NET PredictionFunction, I can't pass in the types for the generic function since they are made at runtime.
I've tried using reflection to call it however it seems to not be able to find the function.
NOTE: Right now the docs for ML.NET is being updated for 0.9.0 so it is unavailable.
What I've tried is this (minimal):
Type[] typeArgs = { generatedType, typeof(ClusterPrediction) };
object[] parametersArray = { mlContext }; // value
MethodInfo method = typeof(TransformerChain).GetMethod("MakePredictionFunction");
if (method == null) { // Using PredictionFunctionExtensions helps here
Console.WriteLine("Method not found!");
}
MethodInfo generic = method.MakeGenericMethod(typeArgs);
var temp = generic.Invoke(model, parametersArray);
The full (revised and trimmed) source (for more context):
Program.cs
namespace Generic {
class Program {
public class GenericData {
public float SepalLength;
public float SepalWidth;
public float PetalLength;
public float PetalWidth;
}
public class ClusterPrediction {
public uint PredictedLabel;
public float[] Score;
}
static void Main(string[] args) {
List<Field> fields = new List<Field>() {
new Field(){ name="SepalLength", type=typeof(float)},
new Field(){ name="SepalWidth", type=typeof(float)},
new Field(){ name="PetalLength", type=typeof(float)},
new Field(){ name="PetalWidth", type=typeof(float)},
};
var generatedType = GenTypeBuilder.CompileResultType(fields);
var mlContext = new MLContext(seed: 0);
TextLoader textLoader = mlContext.Data.TextReader(new TextLoader.Arguments() {
Separator = ",",
Column = new[]
{
new TextLoader.Column("SepalLength", DataKind.R4, 0),
new TextLoader.Column("SepalWidth", DataKind.R4, 1),
new TextLoader.Column("PetalLength", DataKind.R4, 2),
new TextLoader.Column("PetalWidth", DataKind.R4, 3)
}
});
IDataView dataView = textLoader.Read(Path.Combine(Environment.CurrentDirectory, "Data", "flowers.txt"););
var pipeline = mlContext.Transforms
.Concatenate("Features", "SepalLength", "SepalWidth", "PetalLength", "PetalWidth")
.Append(mlContext.Clustering.Trainers.KMeans("Features", clustersCount: 3));
var model = pipeline.Fit(dataView);
Type[] typeArgs = { generatedType, typeof(ClusterPrediction) };
object[] parametersArray = { mlContext }; // value
MethodInfo method = typeof(TransformerChain).GetMethod("MakePredictionFunction");
if (method == null) { // Using PredictionFunctionExtensions helps here
Console.WriteLine("Method not found!");
}
MethodInfo generic = method.MakeGenericMethod(typeArgs);
var temp = generic.Invoke(model, parametersArray);
var prediction = temp.Predict(new GenericData {SepalLength = 5.6f, SepalWidth = 2.5f,
PetalLength = 3.9f, PetalWidth = 1.1f});
}
}
}
Try reading your test data in an IDataView, than pass that IDataView to model.Transform();
That should insert the Score and the PredictedLabel as separate columns in your test data.
It seems, when trying to reflect for the MakePredictionFunction, you confused the TransformerChain<TLastTransformer> type (which is an instantiable generic type) with the static class TransformerChain.
But even reflecting upon TransformerChain<TLastTransformer> will not succeed, because MakePredictionFunction is not a method declared by this type. Rather, MakePredictionFunction is an extension method declared in the static class PredictionFunctionExtensions⁽¹⁾.
Thus, to get the MethodInfo for MakePredictionFunction, try this:
MethodInfo method = typeof(PredictionFunctionExtensions).GetMethod("MakePredictionFunction");
⁽¹⁾
I am not 100% certain which namespace PredictionFunctionExtensions resides in. Searching the ML.NET 0.9.0 API documentation, it seems it resides in the Microsoft.ML.Runtime.Data namespace. But trying to visit the actual documentation page for MakePredictionFunction currently only results in a 404 error, so there is a chance that this information might perhaps not be accurate (i am no ML.NET user, so i can't verify) :-(
is it possible to generate an eventhandler while running?
I want to do something like that:
public bool addCallback(string name, Delegate Callback)
{
EventInfo ei = DataProxy.GetType().GetEvent(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (ei == null)
return false;
ei.AddEventHandler(DataProxy, Callback);
//now I want to add an Eventhandler, which removes the Callback and this new Eventhandler itsself
return true;
}
(I am not 100% sure I understand what you are going to hook your generated event handler to from the example, but here's the easiest way I know of for creating an event handler)
Depends on your platform and trust level. The most flexible way of doing it is to use Emit to generate the method (see here).
However, I found a relatively easy to use and good alternative to be generated Linq expressions (here's the namespace help).
The idea is fairly simple:
Use the various Expression-derived classes you can see in the namespace to define what your callback is doing. In this case, you want to generate something that calls .RemoveEventHandler (I am guessing) on the ei instance (specifically, you will use the ConstantExpression to create a ref to your ei variable and to your Callback parameter and a MethodCallExpression to create a call to the RemoveDataHandler method).
Once you create the expression that does what you need, you need to create a delegate (Lambda) out of it (see here)
Almost done. You still need to compile the lambda, which you do by calling .Compile on the object you got from the previous step (see here)
Edit: This is a Windows console example of a dynamically generated delegate that removes itself. Note that WP7 Linq expression support is more limited than .NET 4.0 and so you will need to adjust it (make helper methods that will do some of the work and call them from the expression instead of what I did).
Edit2: BTW: The mechanism by which the lambda can remove itself, is to create another lambda that returns a local variable that is of that type. After creating the lambda, save it to the local variable and run the code (I am not sure if this would have worked without the secondary lambda)
Edit3: No - you have to use the delegate trick, otherwise, the constant gets "frozen" and will not update as you would want it to. So the code as is works.
public class MyEventArgs : EventArgs
{
}
public class EventContainer
{
public event EventHandler<MyEventArgs> MyEvent;
public void Fire()
{
Console.WriteLine("Firing");
if (MyEvent != null)
{
MyEvent(this, new MyEventArgs());
}
Console.WriteLine("Fired");
}
}
class Program
{
static void Main(string[] args)
{
EventContainer container = new EventContainer();
var adder = container.GetType().GetMethod("add_MyEvent");
var remover = container.GetType().GetMethod("remove_MyEvent");
object self = null;
Func<object> getSelf = () => self;
var block = Expression.Block(
// Call something to output to console.
Expression.Call(
null,
typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),
Expression.Constant("Event called")),
// Now call the remove_Event method.
Expression.Call(
Expression.Constant(container), // passing the container as "this"
remover, // And the remover as the method info
Expression.Convert( // we need to cast the result of getSelf to the correct type to pass as an argument
Expression.Invoke( // For the parameter (what to convert), we need to call getSelf
Expression.Constant(getSelf)), // So this is a ref to getSelf
adder.GetParameters()[0].ParameterType) // finally, say what to convert to.
)
);
// Create a lambda of the correct type.
var lambda = Expression.Lambda(
adder.GetParameters()[0].ParameterType,
block,
Expression.Parameter(typeof(object)),
Expression.Parameter(typeof(MyEventArgs)));
var del = lambda.Compile();
// Make sure "self" knows what the delegate is (so our generated code can remove it)
self = del;
// Add the event.
adder.Invoke(container, new object[] { del });
// Fire once - see that delegate is being called.
container.Fire();
// Fire twice - see that the delegate was removed.
container.Fire();
}
}
public static bool addCallback(string name, Delegate Callback)
{
if (DataProxy == null)
GetDataProxy();
EventInfo ei = DataProxy.GetType().GetEvent(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (ei == null)
return false;
ei.AddEventHandler(DataProxy, Callback);
Type handlerType = ei.EventHandlerType;
MethodInfo invokeMethod = handlerType.GetMethod("Invoke");
ParameterInfo[] parms = invokeMethod.GetParameters();
Type[] parmTypes = new Type[parms.Length];
for (int i = 0; i < parms.Length; i++)
{
parmTypes[i] = parms[i].ParameterType;
}
List<ParameterExpression> parameters = new List<ParameterExpression>();
foreach(Type t in parmTypes)
{
parameters.Add(System.Linq.Expressions.Expression.Parameter(t));
}
ConstantExpression eventInfo = System.Linq.Expressions.Expression.Constant(ei, typeof(EventInfo));
ConstantExpression eventCallback = System.Linq.Expressions.Expression.Constant(Callback, typeof(Delegate));
ConstantExpression dataProxy = System.Linq.Expressions.Expression.Constant(DataProxy, typeof(MAServiceClient));
MethodCallExpression call = System.Linq.Expressions.Expression.Call(eventInfo, ei.GetType().GetMethod("RemoveEventHandler"), dataProxy, eventCallback);
//add to Expression.Body the call, which removes the new Eventhandler itsself
ei.AddEventHandler(DataProxy, System.Linq.Expressions.Expression.Lambda(ei.EventHandlerType, call, parameters).Compile());
return true;
}
This is what my method looks like right now. There is just one step missing, where the new Eventhandler (created by System.Linq.Expressions.Expression.Lambda(ei.EventHandlerType, call, parameters).Compile()) removes itsself (see comment).
Thanks to Shahar Prish I came up with the following code:
using ex = System.Linq.Expressions;
using System.Linq.Expressions;
public static bool addCallback(string name, Delegate Callback)
{
if (DataProxy == null)
GetDataProxy();
EventInfo ei = DataProxy.GetType().GetEvent(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (ei == null)
return false;
ei.AddEventHandler(DataProxy, Callback);
Type handlerType = ei.EventHandlerType;
MethodInfo removeMethod = ei.GetType().GetMethod("RemoveEventHandler");
MethodInfo invokeMethod = handlerType.GetMethod("Invoke");
ParameterInfo[] parms = invokeMethod.GetParameters();
Type[] parmTypes = new Type[parms.Length];
for (int i = 0; i < parms.Length; i++)
{
parmTypes[i] = parms[i].ParameterType;
}
List<ParameterExpression> parameters = new List<ParameterExpression>();
foreach(Type t in parmTypes)
{
parameters.Add(System.Linq.Expressions.Expression.Parameter(t));
}
Delegate self = null;
Func<Delegate> getSelf = () => self;
ConstantExpression eventInfo = ex.Expression.Constant(ei, typeof(EventInfo));
ConstantExpression eventCallback = ex.Expression.Constant(Callback, typeof(Delegate));
ConstantExpression dataProxy = ex.Expression.Constant(DataProxy, typeof(MAServiceClient));
MethodCallExpression removeCallback = ex.Expression.Call(eventInfo, removeMethod, dataProxy, eventCallback);
MethodCallExpression removeSelf = ex.Expression.Call(eventInfo, removeMethod, dataProxy, ex.Expression.Invoke(ex.Expression.Constant(getSelf)));
BlockExpression block = ex.Expression.Block(removeCallback, removeSelf);
LambdaExpression lambda = ex.Expression.Lambda(ei.EventHandlerType, block, parameters);
Delegate del = lambda.Compile();
self = del;
ei.AddEventHandler(DataProxy, del);
lambda = ex.Expression.Lambda(ei.EventHandlerType, block, parameters);
return true;
}
As I said before, this method should add the Eventhandler passed by Delegate Callback to the Event named string name of the static MAServiceClient DataProxy and remove it after it was called (and the Eventhandler which removes the Callback itsself).
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);
}
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]);