Handling ONNX/ML.NET models with generic interfaces - c#

i'm kind of struggling with a ML.NET related problem here and am hoping that someone might can help me.
I'm developing an (.NET core) application which consumes ONNX models whose inputs are unknown at compile time. What I've done so far:
I'm able to compile an assembly during runtime which contains the input class definition and load this definition:
var genericSampleAssembly =
AssemblyLoadContext.Default.LoadFromAssemblyPath("/app/storage/sample.dll");
Type genericInputClass = genericSampleAssembly.GetType("GenericInterface.sample");
Also I'm able to train a model with that dynamically created Inputtype using Reflection:
MethodInfo genericCreateTextLoader = typeof(TextLoaderSaverCatalog).GetMethods()
.Where(_ => _.Name == "CreateTextLoader")
.Single(_ => _.GetParameters().Length == 6)
.MakeGenericMethod(_genericInputClass);
TextLoader reader = genericCreateTextLoader.Invoke(_mlContext.Data, new object[] { _mlContext.Data, false, ',', true, true, false}) as TextLoader;
IDataView trainingDataView = reader.Read("sample.txt");
var debug = trainingDataView.Preview();
var pipeline = _mlContext.Transforms.Concatenate("Features", _featureNamesModel
.AppendCacheCheckpoint(_mlContext)
.Append(_mlContext.Regression.Trainers.StochasticDualCoordinateAscent(labelColumn: "Label",
featureColumn: "Features")));
ITransformer model = pipeline.Fit(trainingDataView);
But I'm not able to make predictions by now because I don't know how to invoke the PredictionEngine. I'm able to get a generic version of that CreatePredictionEngine method but don't now how to cast that returning object to an PredictionEngine and finally invoke the Predict method:
MethodInfo genericCreatePredictionEngineMethod = typeof(PredictionEngineExtensions).GetMethods()
.Single(_ => _.Name == "CreatePredictionEngine")
.MakeGenericMethod(new Type[] { genericInputClass, typeof(GenericPrediction)});
var predictionEngine = genericCreatePredictionEngineMethod.Invoke(_model, new object[] {_model, _mlContext, null, null});
predictionEngine is of Type object in this case but I need to cast it to something like PredictionEngine<genericInputClass, GenericPrediction>, while genericInputClass is the class from that dynamically created assembly and GenericPrediction is a simple class with one output I know at compile time.
So whats missing is something like:
MethodInfo genericCreatePredictionEngineMethod = typeof(PredictionEngineExtensions).GetMethods()
.Single(_ => _.Name == "CreatePredictionEngine")
.MakeGenericMethod(new Type[] { genericInputClass, typeof(GenericPrediction)});
PredictionEngine<genericInputClass, GenericPrediction> predictionEngine = genericCreatePredictionEngineMethod.Invoke(_model, new object[] {_model, _mlContext, null, null}) as PredictionEngine<genericInputClass, GenericPrediction>;
float prediction = predictionEngine.Predict(genericInputClass inputValue);
Does anyone had a similar problem or has any other hints?
I might missed some lines, because I copy/pasted and simplified it pretty quick. In case something is missing, I'll provide it later.
EDIT: I constructed a minimal example to show the basic problem. As mentioned in the comments dynamic isn't possible due to the ML.NET methods.
using System;
using System.Linq;
using System.Runtime.Loader;
namespace ReflectionSample
{
class Program
{
static void Main(string[] args)
{
// Example with a known Type
var extendedClass = new DummyExtendedClass();
SampleGenericClass<String> sampleGenericClass = extendedClass.SampleGenericExtensionMethod<String>();
sampleGenericClass.SampleMethod("");
// At compile time unknown Type - In reality the loaded dll is compiled during runtime
var runtimeCompiledSampleAssembly =
AssemblyLoadContext.Default.LoadFromAssemblyPath("C:/Program Files (x86)/Reference Assemblies/Microsoft/Framework/.NETCore/v4.5/System.IO.dll");
var compileTimeUnknownClass = runtimeCompiledSampleAssembly.GetType("System.IO.TextReader");
var reflectedExtensionMethod = typeof(Extensions).GetMethods()
.Single(_=>_.Name== "SampleGenericExtensionMethod")
.MakeGenericMethod(new[] {compileTimeUnknownClass});
var howToCastThis = reflectedExtensionMethod.Invoke(extendedClass, new object[] {extendedClass});
// whats missing:
// howToCastThis is of Type object but should be SampleGenericClass<System.IO.TextReader>
// I need to be able to call howToCastThis.SampleMethod(new System.IO.TextReader)
// I thought this might work via reflecting SampleMethod and MakeGenericMethod
Console.ReadKey();
}
}
public sealed class SampleGenericClass<T>
{
public void SampleMethod(T typeInput)
{
Console.WriteLine($"Invoking method worked! T is of type {typeof(T)}");
}
}
public static class Extensions
{
public static SampleGenericClass<T> SampleGenericExtensionMethod<T>(this DummyExtendedClass extendedClass)
{
Console.WriteLine($"Invoking extension method worked! T is of type {typeof(T)}");
return new SampleGenericClass<T>();
}
}
public class DummyExtendedClass
{
public DummyExtendedClass() { }
}
}
Greetings
Sven

Nice job on the MCVE. I was able to invoke SampleMethod; it turns out there's not much to it and it's probably less complicated than you imagined.
In your example, the object you're getting, howToCastThis, is of a type that's already close-constructed. As long as you start from that instance's type, you don't need to use MakeGenericMethod.
Let's say you have an object instance, compileTimeTypeUnknownInstance, for the parameter you want to pass to SampleMethod. Since System.IO.TextReader is abstract, compileTimeTypeUnknownInstance would have to be of a concrete, TextReader-derived type. With those conditions satisfied, the following works:
var sampleMethod = howToCastThis.GetType().GetMethods()
.Single(mi => mi.Name == "SampleMethod");
sampleMethod.Invoke(howToCastThis, new object[] { compileTimeTypeUnknownInstance });
SampleMethod reports that T is of type System.Text.TextReader.
Again, howToCastThis is of a close-constructed type, therefore, so is the method you want.
Note: Although not the case here, a method in a close-constructed type can introduce additional type arguments so you still would have to call MakeGenericMethod to close-construct the method in that case.
Now, if I were to try to translate this to your situation, I'm guessing it would look something like this:
var predictMethod = predictionEngine.GetType().GetMethods()
.Single(mi => mi.Name == "Predict");
float prediction = (float)predictMethod.Invoke(predictionEngine, new object[] { inputValue });
I'm not sure about the syntax in your pseudocode call to Predict. I assumed that inputValue was the only parameter and genericInputClass was only there to indicate that it was a type argument in the close-constructed type. If this was incorrect, you'll need to figure out what actually goes in that object[] argument.

Related

How to instantiate a Lazy<Square> using runtime type info, and a function that only returns Shape?

I want to create a Lazy<> with runtime information about the contained type, but I'm not sure how to create the required Func<> initializer. I feel the answer is somewhere in Delegate.CreateDelegate, but I couldn't see how to make that work. The below illustrates what I want to do:
class ShapeTools {
abstract class Shape {}
class Square : Shape {}
Lazy<Square> aLazyShape;
ShapeTools() {
setup(GetType().GetFields().Where(f => f.Name == "aLazyShape").First());
}
// returns a shape matching the provided type (unimplemented)
Shape GetShape(Type shapeType) { return null; }
void setup (FieldInfo field) { // aLazyShape
var funcType = typeof(Func<>).MakeGenericType(field.FieldType); // = typeof(Func<Square>)
var shapeType = funcType.GetGenericArguments().First(); // = typeof(Square)
// var myFunc = Activator.CreateInstance(funcType, () => { return GetShape(shapeType); }) // doesn't compile - type doesn't match
var lazy = Activator.CreateInstance(field.FieldType, myFunc); // This takes a Func<Square>
field.SetValue(this, lazy);
}
}
You can achieve what you're looking for by using System.Linq.Expressions. Why you want to do it this way is another thing. =)
See this other answer for where the key parts about using Expression came from.
Note that your code for creating var funcType was not returning typeof(Func<Square>), but instead typeof(Func<Lazy<Square>>); I fixed that. Most things were made public for convenience when compiling. You could change the access to GetShape if you want to update BindingFlags in the call to GetMethod, for example.
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace SomeNamespace
{
class Program
{
static void Main(string[] args)
{
ShapeTools st = new ShapeTools();
ShapeTools.Square s = st.aLazyShape.Value;
// s will be whatever ShapeTools.GetShape returns
}
public class ShapeTools
{
public abstract class Shape { }
public class Square : Shape { }
public Lazy<Square> aLazyShape;
public ShapeTools()
{
setup(GetType().GetFields().Where(f => f.Name == "aLazyShape").First());
}
// returns a shape matching the provided type (unimplemented, just an example)
public static object GetShape(Type shapeType) { return new Square(); }
void setup(FieldInfo field)
{ // only handles 'aLazyShape' right now
Type funcType = typeof(Func<>).MakeGenericType(field.FieldType.GenericTypeArguments[0]); // = typeof(Func<Square>)
Type shapeType = funcType.GetGenericArguments().First(); // = typeof(Square)
// get MethodInfo for the static method in this class that returns the right shape
MethodInfo getInstanceOfType = GetType().GetMethod(nameof(GetShape));
// build the Func dynamically
var typeConst = Expression.Constant(shapeType); // get the shapeType as an Expression
var callGetInstance = Expression.Call(getInstanceOfType, typeConst); // invoke our (static) method to get the instance of shape
var cast = Expression.Convert(callGetInstance, shapeType); // cast the return of our method (which is object) to the right type
var toLambda = Expression.Lambda(cast); // wrap everything in a Lambda to return our instance
var finalFunc = toLambda.Compile(); // compile to the final Func
var lazy = Activator.CreateInstance(field.FieldType, finalFunc); // now create the Lazy<T>, where T is Square
field.SetValue(this, lazy);
}
}
}
}
Lastly, notice that GetShape was made static. This was for convenience when using Expressions - if you want, you can pass in an instance of ShapeTools to the Expressions code instead.
And as written, ShapeTools.setup is just an example of how this can work. I assume you'd want to clean it up to handle other field types than just Lazy<Shape>.
// var myFunc = Activator.CreateInstance(funcType, () => { return GetShape(shapeType); }) // doesn't compile - type doesn't match
C# is a statically typed language, and as such every type has to be fully known at compile time. As you discovered, while you can ask the runtime to create you any type you build the definition for (with a Type object), the return type has to be fully known -- the function you call returns object for that very reason.
You have a few options here:
Stay on the object level, and use further reflection calls to consume your newly created object. Large performance hit, of course.
Go dynamic, which creates a compiler site to translate any calls through it. It also spreads like a plague, anything that uses dynamic returns dynamic, so until you get something you know the type of you'll be using this kind of calls. Amortized moderate performance hit, very high performance hit during the first JIT compilation.
Use interfaces. You have a base class already, if you define it smart enough, that should be all you need to use your object. That myfunc function would be of type Func<Shape> in that case, and it obviously can only access the Shape portion of your object, though you're free to try casting using is/as as needed, usually in ifs. Very low performance hit, but you need to design your type smart (which you should be doing anyway).

Call function in dynamic linq

I'm trying to call a function in a dynamic linq select statement, but im getting error:
No property or field 'A' exists in type 'Tuple2'
Example code:
void Main()
{
var a = new Tuple<int, int>(1,1);
var b = new[]{ a };
var q = b.AsQueryable().Select("A.Test(it.Item1)");
q.Dump();
}
public static class A
{
public static int Test(int i)
{
return i++;
}
}
How should I change my code to get this working?
If I call built in function Convert.ToInt32 for example it works fine.
var q = b.AsQueryable().Select("Convert.ToInt32(it.Item1)");
Also how do I cast a property using dynamic linq?
var q = b.AsQueryable().Select("((float)it.Item1)");
I'll say that the dynamic-linq isn't "strong enough" to do these things. It looks for methods only in the given objects and some special classes: Math, Convert, the various base types (int, float, string, ...), Guid, Timespan, DateTime
The list of these types is clearly visible if you use ilspy/reflector on the file. They are in System.Linq.Dynamic.ExpressionParser.predefinedTypes .
Now, clearly I could be wrong, but this works: .Select("Guid.NewGuid().ToString()").Cast<string>().ToArray()
showing that it's quite probable that that is the "correct" list.
There is an article here on how to modify Dynamic LINQ if you are interested http://www.krizzcode.com/2012/01/extending-dynamiclinq-language.html
Now, an intelligent man would take the source of dynamic linq and simply expand that array... But here there aren't intelligent men... There are only programmers that want blood! Blood but especially innards!
var type = typeof(DynamicQueryable).Assembly.GetType("System.Linq.Dynamic.ExpressionParser");
FieldInfo field = type.GetField("predefinedTypes", BindingFlags.Static | BindingFlags.NonPublic);
Type[] predefinedTypes = (Type[])field.GetValue(null);
Array.Resize(ref predefinedTypes, predefinedTypes.Length + 1);
predefinedTypes[predefinedTypes.Length - 1] = typeof(A); // Your type
field.SetValue(null, predefinedTypes);
Do this (with the types you want) BEFORE the first call to Dynamic Linq (because after the first call the methods/properties of these types are cached)
Explanation: we use reflection to add our object(s) to this "special list".
I know there is already an accepted answer on this but it did not work for me. I am using Dynamic Linq 1.1.4. I wanted to do a query like this
$.GetNewestRisk() == null
Where GetNewestRisk() is a public method on the object represented by $. I kept getting this error "Error running query, Methods on type 'Patient' are not accessible (at index 2)".
I found in the source code there is a GlobalConfig object that allows a custom provider to be assigned which will hold all of the types you may want to work with. Here is the source code for the custom provider:
public class CustomTypeProvider: IDynamicLinkCustomTypeProvider
{
public HashSet<Type> GetCustomTypes()
{
HashSet<Type> types = new HashSet<Type>();
types.Add(typeof(Patient));
types.Add(typeof(RiskFactorResult));
types.Add(typeof(PatientLabResult));
types.Add(typeof(PatientVital));
return types;
}
}
Here is how I am using it:
System.Linq.Dynamic.GlobalConfig.CustomTypeProvider = new CustomType();
After making this call I am able to call methods on the objects inside of the expression.
#xanatos answer doesn't work for .Net Core version. So I've found something similar related by #Kent on the System.Dynamic.Linq.Core tests DynamicExpressionParserTests written by the library's author himself.
The given TestCustomTypeProviderClass allows you to use the DynamicLinqType class annotation which is pretty usefull for this problem.
To answer to question, you then just needed to defined the class (ensure to annotate with DynamicLinqType) :
[DynamicLinqType]
public static class A
{
public static int Test(int i)
{
return i++;
}
}
Add a customTypeProvider as mentioned above :
private class TestCustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider
{
private HashSet<Type> _customTypes;
public virtual HashSet<Type> GetCustomTypes()
{
if (_customTypes != null)
{
return _customTypes;
}
_customTypes = new HashSet<Type>(FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly }));
return _customTypes;
}
}
and use a ParsingConfig with the configurable Select to call it :
var config = new ParsingConfig
{
CustomTypeProvider = new TestCustomTypeProvider()
};
var q = b.AsQueryable().Select(config, "A.Test(it.Item1)");
#Armand has put together a brilliant solution for this issue, and being the only solution I was able to find regarding this I want to add to it for anyone who tries the same approach.
The class that is marked with...
[DynamicLinqType]
... must be taken into consideration when you run the following line:
FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly })
In the solution provided above, this assumes the class that contains the function to be evaluated is on the same class the code currently resides in. If the methods are to be used outside of said class, the assembly will need to change.
FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { typeof(AnotherClassName).Assembly })
Nothing changes from the solution above, this is just for clarification for anyone attempting to use it.
As regards the current version (1.2.19) of Dynamic LINQ, you will probably get another exception:
System.Linq.Dynamic.Core.Exceptions.ParseException : Enum value 'Test' is not defined in enum type 'A'
To make DLINQ know your type 'A', you have two options:
Set up parsing config with your own custom types provider where you directly specify the type 'A'.
Mark your type with the attribute [DynamicLinqType]. If that type is loaded into the current domain (that's the usual case), you don't have to do anything more since the default custom type provider already scans the current AppDomain for types marked with [DynamicLinqType]. And only if that's not the case, i.e. your type is not loaded into the current domain, you have to do something like in that answer.
What if you would like to use both approaches - the first for type 'A' and the second for type 'B'? In that case, you just have to "merge" your type 'A' with the default provider types:
public class DynamicLinqTests
{
[Test]
public void Test()
{
var a = new Tuple<int, int>(1, 1);
var b = new[] { a };
var parsingConfig = new ParsingConfig
{
ResolveTypesBySimpleName = true,
CustomTypeProvider = new TestCustomTypesProvider()
};
var queryWithA = b.AsQueryable().Select(parsingConfig, "A.Test(it.Item1)");
queryWithA.ToDynamicList();
var queryWithB = b.AsQueryable().Select(parsingConfig, "B.Test(it.Item1)");
queryWithB.ToDynamicList();
}
public static class A
{
public static int Test(int i)
{
return i++;
}
}
[DynamicLinqType]
public static class B
{
public static int Test(int i)
{
return i++;
}
}
public class TestCustomTypesProvider : DefaultDynamicLinqCustomTypeProvider
{
public override HashSet<Type> GetCustomTypes()
{
var customTypes = base.GetCustomTypes();
customTypes.Add(typeof(A));
return customTypes;
}
}
}
I may be confused but your syntax whereby you are using a string in your Selects doesn't compile for me. The following syntax works:
var q = b.AsQueryable().Select(it => A.Test(it.Item1));
var b = new[]{ a };
The above array is don't know what type of array , and it's not type safe ?
Your values are assigned in variant data type so it's not integer value (I think string value) ,when you get this values in your query must need to convert.toint32() because your class parameter data type is integer
Please try it
var b = new **int**[]{ a };
instead of var b = new[]{ a };
The important hint is here (in bold):
No property or field 'xxx' exists in **type** 'xxx'
And Please look this for previous discussion :
Dynamic Linq - no property or field exists in type 'datarow'
The following works for me:
var a = new Tuple<int, int>(1, 1);
var b = new[] { a };
var q = b.AsQueryable().Select(it=>A.Test(it.Item1));
var q1 = b.AsQueryable().Select(it => Convert.ToInt32(it.Item1));
var q2 = b.AsQueryable().Select(it => (float) it.Item1);

Create a lambda expression with a new anonymous type at runtime

I want to invoke a method that expects a parameter like this:
Expression<Func<sometype, 'a>> expr
I need to construct this parameter at runtime, because I won't know what the anonymous type will look like before; it could have any amount of fields:
x => new { a=x.a, b=x.b, c=x.c, etc... }
I can create a type at runtime that has the same 'signature' (Is that the correct word for this?) as the desired anonymous type, but the question is: How do I construct this lambda expression at runtime from that? Especially Expression.New is bugging me, because I need to pass a constructorInfo to it that I have to get from an existing type (which can indeed be an anonymous type, but I can't create an anonymous type at runtime. Or is there a way to do that?).
Update (some context as requested in the comments)
The method I want to invoke is:
DependentNavigationPropertyConfiguration.HasForeignKey<TKey>(Expression<Func<TDependentEntityType, TKey>> foreignKeyExpression)
The reason I want to do this is to automatically make a navigation property to an entity that inherits from a certain base class include the key of that base class in the foreign key. Because an entity can have multiple key fields of any type, the type TKey is only known to me at runtime.
Use a separate method:
public static void Main()
{
var myExpression = Express(str => new {
String = str,
Length = str.Length
});
// We can compile/use it as well...
var compiledExpression = myExpression.Compile();
var anonymousOutput = compiledExpression("Input String");
Console.WriteLine(anonymousOutput.String); // Output: Input String
Console.WriteLine(anonymousOutput.Length); // Output: 12
Debug.WriteLine(myExpression); // Output: "str => new <>f__AnonymousType0`2(String = str, Length = str.Length)"
Console.ReadLine();
}
static Expression<Func<String, T>> Express<T>(Expression<Func<String, T>> expression)
{
return expression;
}
Note however, that the starting type (in my example String) must be known up front.
Update:
Since what it sounds like you're trying to do is dynamically create a type, I'll give you a simple example of how to do that.
public static void Main()
{
// Create an anonymous type with two fields
Type myAnonymousType = CreateNewType<String, Int32>();
dynamic myAnon = Activator.CreateInstance(myAnonymousType);
myAnon.FieldA = "A String";
myAnon.FieldB = 1234;
Console.WriteLine(myAnon.FieldA); // Output : "AString"
Console.WriteLine(myAnon.FieldB); // Output : 1234
Console.ReadLine();
}
public static Type CreateNewType<TFieldTypeA, TFieldTypeB>()
{
// Let's start by creating a new assembly
AssemblyName dynamicAssemblyName = new AssemblyName("MyAsm");
AssemblyBuilder dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule("MyAsm");
// Now let's build a new type
TypeBuilder dynamicAnonymousType = dynamicModule.DefineType("MyAnon", TypeAttributes.Public);
// Let's add some fields to the type.
FieldInfo dynamicFieldA = dynamicAnonymousType.DefineField("FieldA", typeof(TFieldTypeA), FieldAttributes.Public);
FieldInfo dynamicFieldB = dynamicAnonymousType.DefineField("FieldB", typeof(TFieldTypeB), FieldAttributes.Public);
// Return the type to the caller
return dynamicAnonymousType.CreateType();
}
As you can see, this is a little more complicated. If you want to study the topic further though, definitely reference Reflection.Emit.
Anonymous types are a compiler feature. If you don't get the compiler to create them at compile-time, then you will have to use meta-programming - either TypeBuilder or maybe CSharpCodeProvider. You might be better off using tuples - at least they are easy to create (you can use Tuple.Create easily enough).
As for the expression; I would suggest typing it as Expression<Func<sometype, object>> - which will work for any formulation. The code inspecting the Expression can of course see what the actual type is.
you can do simply like this
context.Students.Join(context.Courses, a => a.Course_id, b => b.Course_id, (a, b) => new { Student= a, Course= b }).Where(x => x.Student_id == studentId)
.Select(y => new
{
StudentId = y.Student.StudentId,
RegistrationNumber = y.Student.RegNo,
Name = y.Student.Name,
Coursename = y.Course.Name
}).ToList();

PrivateObject wont return out parameter

I'm trying to test private methods in a Unit test project. So far it's going great, but I hit a bump, when I have to test a method with an out parameter. The signature for that method is:
private bool GotSSI(out SSI ssi, RSI rsi)
{
~code omitted~
}
And the unittest (the part that is not working) looks like this:
SSI ssi = null;
object[] p = new object[]{ssi,rsi};
Type[] t = new Type[] { typeof(SSI).MakeByRefType(), typeof(RSI) };
actual = (bool) privateTarget.Invoke("GotSSI",t,p);
The GotSSI method work. I've tested it in debug mode within the unit test and I can see that the 'ssi' out variable is set inside the method, before returning it's true or false value. But when the test returns to it's own code, the 'ssi' variable is still null. So the problem is that the object I created in the "GotSSI" method, is not parsed out of the PrivateObject invoke method.
Anyone knows what I am missing?
Update (Solution by Rafal)
Rafal's solution work perfectly and here is how I implemented the solution.
I created a delegate:
delegate bool GotSSIInternal(out SSI ssi, RSI rsi);
And when I have created the object I wanted to test, I build the delegate (target is the object i'm testing):
GotSSIInternal gotSSIInternal = (GotSSIInternal) Delegate.CreateDelegate(
typeof (GotSSIInternal),
target,
typeof(OfflineResolver).GetMethod("GotSSI", BindingFlags.NonPublic | BindingFlags.Instance));
After that is very simple to call the delegate:
actual = gotSSIInternal.Invoke(out ssi, rsi);
The solution is very simple and works like a charm.
Although the final solution that was accepted works, there is a much simpler way to do it. If you follow the link given in the accepted answer from Rafal, you will find a similar question to this one, with two answers. The second answer (with the most "useful" points) is the simpler of the two.
Here is a modified version of that answer specifically for a testing scenario:
//method to test is a private method of the class ClassTotest with signature
// TryGetCustomerData(List<Invoice> invoices, out CustomerData customerData)
//set up
var objToTest = new ClassToTest();
var invoices = GetInvoices();
CustomerData customerData = null;
//set up the arguments
var args = new object[] { invoices, customerData };
//get the MethodInfo of the method you want to test
var method = typeof(ClassToTest).GetMethod("TryGetCustomerData",
BindingFlags.NonPublic | BindingFlags.Instance);
//invoke it
var success = (bool)method.Invoke(objToTest, args);
//get back the customerData argument - the "out" parameter
var actual = args[1] as CustomerData;
your invocation of method with out parameter is wrong if you want to get the out value. See this on how to invoke it with reflection.
You need to ask yourself if you really need to test private methods? I personally do not test Private methods but it is all down to personal opinion (and it can get quite heated). There are numerous reasons / articles / opinions. A good SO thread can be found here.
An excerpt from the accepted answer is "A private method is an implementation detail that should be hidden to the users of the class. Testing private methods breaks encapsulation..."
The reason I do not test my private methods is because they are more likely to change than the public interface. If you cover all of your private methods it makes refactoring more complex (again, just my opinion). If you change a private method and the Public interface breaks you will know as your unit tests fail and you can then drill down.
That is just my opinion and I know many disagree so just putting it out there!
I can use PrivateObject to do this, although it is not technically a "return value", like:
object[] p = new object[] { null, rsi };
Type[] t = new Type[] { typeof(SSI).MakeByRefType(), typeof(RSI) };
actual = (bool)privateTarget.Invoke("GotSSI", t, p);
SSI ssi = p[0];
Actually the type array can be omitted if the method overload can be determined without it:
object[] p = new object[] { null, rsi };
actual = (bool)privateTarget.Invoke("GotSSI", p);
SSI ssi = p[0];
Just do this: https://instance-factory.com/?p=738
ClassWithPrivateMethods instance = new ClassWithPrivateMethods();
PrivateObject privateObject = new PrivateObject(instance);
object[] args
= new object[] { "some value", null /* placeholder for out param */ };
privateObject.Invoke("PrivateMethodHavingOutParameter", args);
var outParameterValue = args[1];

Why can I not bind a DynamicMethod to a struct instance?

DynamicMethods allow you to specify a target instance for the delegate you create. However, it appears that this does not work when you use a struct type. It fails with an exception telling me it cannot bind to this method. Is the error because my IL does not unbox the target instance?
If I change A here to a class it works without issue. What am I doing wrong?
(Also please do not suggest calling Delegate.CreateDelegate to bind to the GetType method with a target instance)
Here is a sample repro:
struct A { }
... //then some where in code::
Func<Type> f = CodeGen.CreateDelegate<Func<Type>>(il=>
il.ldarga_s(0)
.constrained(typeof(A))
.callvirt(typeof(object).GetMethod("GetType"))
.ret(),
name:"Constrained",
target:new A()
);
Note: I'm using the Emitted library for the fluent interface for IL. Also Here is the code for the CodeGen Method.
public static class CodeGen
{
public static TDelegate CreateDelegate<TDelegate>(Action<ILGenerator> genFunc, string name = "", object target = null, bool restrictedSkipVisibility = false)
where TDelegate:class
{
ArgumentValidator.AssertGenericIsDelegateType(() => typeof(TDelegate));
ArgumentValidator.AssertIsNotNull(() => genFunc);
var invokeMethod = typeof(TDelegate).GetMethod("Invoke");
var #params = invokeMethod.GetParameters();
var paramTypes = new Type[#params.Length + 1];
paramTypes[0] = target == null ? typeof(object) : target.GetType();
#params.ConvertAll(p => p.ParameterType)
.CopyTo(paramTypes, 1);
var method = new DynamicMethod(name ?? string.Empty, invokeMethod.ReturnType, paramTypes, restrictedSkipVisibility);
genFunc(method.GetILGenerator());
return method.CreateDelegate<TDelegate>(target);
}
}
See the important note at http://msdn.microsoft.com/en-us/library/74x8f551.aspx, which also applies here:
If method is static (Shared in Visual Basic) and its first parameter
is of type Object or ValueType, then firstArgument can be a value
type. In this case firstArgument is automatically boxed. Automatic
boxing does not occur for any other arguments, as it would in a C# or
Visual Basic function call.
The implication is that the first argument to your dynamic method will need to be of type object, and you'll need to do a ldarg_0 followed by an unbox before doing the constrained call.

Categories