Getting System.CommandLine to accept compiled lambda as handler - c#

I'm trying to migrate a project from CommandLineParser to newer System.CommandLine.
CommandLineParser uses attributes on the handler classes and attributes on properties for options, while System.CommandLine expects all verbs and options to be part of a RootCommand.
What I'm trying to do is emulate the attribute structure and creating ad-hoc Handlers for my classes, so I don't have to locate each attribute and make it work with the newer package, also it lacks extensibility imho.
The problem: System.CommandLine doesn't seem to accept my built delegate (using LINQ Expressions) because of the way it tries to bind options to delegate parameters, it uses reflection on Delegate.Method.GetParameters(), and in the case of built delegates, this includes a System.Runtime.CompilerServices.Closure type parameter, which totally baffles the binding process.
I also tried to call the CommandHandler builder that takes the delegate methodinfo and target, to be welcomed with another error:
foreach (var verbType in Assembly.GetExecutingAssembly().GetTypes().Where(t => t.GetCustomAttributes(typeof(VerbAttribute), false).Length > 0))
{
var attr = verbType.GetCustomAttribute<VerbAttribute>();
var verb = new Command(attr.Verb, attr.HelpText);
IEnumerable<PropertyInfo> props = verbType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy).Where(pi => pi.GetCustomAttributes(typeof(OptionAttribute), true).Length > 0);
MethodInfo runMethod = verbType.GetMethod("Run", BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
Delegate handler = BuildDelegate(props, runMethod, verbType);
verb.Handler = CommandHandler.Create(handler); // Runtime Error trying to bind Delegate.Method.GetParameters()
verb.Handler = CommandHandler.Create(handler.Method, handler.Target); // runtime error: "MethodInfo must be a runtime MethodInfo object"
}
I'd like to know if this parameter can be eliminated, or what could be done to migrate.
This is my code to build delegates: (expects a Run() method to be created on the handler class)
private static readonly TextInfo _textInfo = CultureInfo.InvariantCulture.TextInfo;
// props: the discovered properties with an Option attribute
// runMethod: the discovered Run method
// instanceType: Type of the handler class
private static Delegate BuildDelegate(IEnumerable<PropertyInfo> props, MethodInfo runMethod, Type instanceType)
{
List<ParameterExpression> parms = new();
List<MemberBinding> binds = new();
foreach (PropertyInfo pi in props)
{
ParameterExpression parm = Expression.Parameter(pi.PropertyType, _lowerCamel(pi.Name));
parms.Add(parm);
MemberBinding bind = Expression.Bind(pi, parm);
binds.Add(bind);
}
Expression self = Expression.MemberInit(Expression.New(instanceType), binds);
LambdaExpression lambda = Expression.Lambda(_getCall(self, runMethod), parms);
return lambda.Compile();
static Expression _getCall(Expression self, MethodInfo runMethod)
{
IEnumerable<Expression> runArgs =
runMethod.GetParameters().Select(pInfo => Expression.Constant(pInfo.DefaultValue, pInfo.ParameterType));
return Expression.Call(self, runMethod, runArgs);
}
static string _lowerCamel(string s)
{
string title = _textInfo.ToTitleCase(_textInfo.ToLower(s));
char[] chars = s.ToCharArray();
chars[0] = _textInfo.ToLower(chars[0]);
return new string(chars);
}
}

Related

Creating RunTime Mock: InvalidProgramException after compiling Expression.Throw with Expression.Throw

I am trying to create a run-time mock, which will have a specific behaviour for every method, which will be called. For example this mock should always return null - which is already working - or raise an exception for a not reachable database or in this specific case throw an Argument Exception.
I know, that this could be done fairly easy with a mocking framework. But since I have to use this via Spring.NET, I can not use a mocking framework, as far as I know. This is because I have to give a type into the StaticApplicationContext. If there is an easier way to create mock types, let me know.
The code below will throw an Expected:
But was: (Common Language Runtime detected an invalid program.)
at TypeMock.Foo()
at TypeBuilderTest.
public interface IInterfaceWithObject
{
String Foo();
}
[Test]
public void MinimalExample()
{
//build expression
var constructorInfo = typeof(ArgumentNullException).GetConstructor(new Type[0]);
Assert.IsNotNull(constructorInfo);
Expression expression = Expression.Throw(Expression.New(constructorInfo));
//create type
// based upon https://stackoverflow.com/questions/38345486/dynamically-create-a-class-by-interface
var ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run);
var mb = ab.DefineDynamicModule("Test");
TypeBuilder typeBuilder = mb.DefineType("TypeMock");
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
typeBuilder.AddInterfaceImplementation(typeof(IInterfaceWithObject));
List<MethodInfo> methods = new List<MethodInfo>(typeof(IInterfaceWithObject).GetMethods());
foreach (Type interf in typeof(IInterfaceWithObject).GetInterfaces())
{
foreach (MethodInfo method in interf.GetMethods())
if (!methods.Contains(method))
methods.Add(method);
}
foreach (var imethod in methods)
{
var parameters = imethod.GetParameters();
List<Type> parameterTypes = new List<Type>();
foreach (var parameter in parameters)
{
parameterTypes.Add(parameter.ParameterType);
}
var method =
typeBuilder.DefineMethod
(
"##" + imethod.Name,
MethodAttributes.Public | MethodAttributes.Static,
imethod.ReturnType,
parameterTypes.ToArray()
);
var thisParameter = Expression.Parameter(typeof(IInterfaceWithObject), "this");
var bodyExpression = Expression.Lambda
(expression
,
thisParameter
);
bodyExpression.CompileToMethod(method);
var stub =
typeBuilder.DefineMethod(imethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, imethod.ReturnType, parameterTypes.ToArray());
var il = stub.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, method, null);
il.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(stub, imethod);
}
Type type = typeBuilder.CreateType();
//create instance via spring.net
StaticApplicationContext context = new StaticApplicationContext();
MutablePropertyValues value = new MutablePropertyValues();
string objectName = "Integer";
context.RegisterSingleton(objectName, type, value);
var objectInstance = (IInterfaceWithObject)context.GetObject(objectName);
Assert.Throws<ArgumentNullException>(() => objectInstance.Foo());
}

Dynamic select projection expression

I have the following class, for which usage is not important. What is important is method SetCacheItemSelector which takes one parameter, a select expression that projects Account entity to AccountCacheDTO:
public class AccountRepositoryCache : RepositoryCache<Account, AccountCacheDTO>
{
public AccountRepositoryCache()
{
SetCacheItemSelector(x => new AccountCacheDTO
{
Id = x.Id,
Login = x.Login
});
}
}
So signature for this method is:
public void SetCacheItemSelector(Expression<Func<TEntity, TCacheItem>> selector)
In this case, TEntity is Account class, and TCacheItem is AccountCacheDTO class.
Is there a way to use reflection to build select expression dynamically for all the properties that are matching for both Account class and AccountCacheDTO class?
Goal is to have method that would look like this:
public Expression<Func<TEntity, TCacheItem>> BuildSelector<TEntity, TCacheItem>()
{
... // implementation with reflection goes here
}
EDIT:
Here is final implementation (pretty much the same as the accepted answer):
public static Expression<Func<TSource, TTarget>> BuildSelector<TSource, TTarget>()
{
Type targetType = typeof(TTarget);
Type sourceType = typeof(TSource);
ParameterExpression parameterExpression = Expression.Parameter(sourceType, "source");
List<MemberBinding> bindings = new List<MemberBinding>();
foreach (PropertyInfo sourceProperty in sourceType.GetProperties().Where(x => x.CanRead))
{
PropertyInfo targetProperty = targetType.GetProperty(sourceProperty.Name);
if (targetProperty != null && targetProperty.CanWrite && targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
{
MemberExpression propertyExpression = Expression.Property(parameterExpression, sourceProperty);
bindings.Add(Expression.Bind(targetProperty, propertyExpression));
}
}
NewExpression newExpression = Expression.New(targetType);
Expression initializer = Expression.MemberInit(newExpression, bindings);
return Expression.Lambda<Func<TSource, TTarget>>(initializer, parameterExpression);
}
I didn't test it, but you should be able to do something like: This is just to convey a general idea and you should be able to tweak it for your requirements.
public Expression<Func<TEntity, TCacheItem>> BuildSelector<TEntity, TCacheItem>(TEntity entity)
{
List<MemberBinding> memberBindings = new List<MemberBinding>();
MemberInitExpression body = null;
foreach (var entityPropertyInfo in typeof(TEntity).GetProperties())
{
foreach (var cachePropertyInfo in typeof(TCacheItem).GetProperties())
{
if (entityPropertyInfo.PropertyType == cachePropertyInfo.PropertyType && entityPropertyInfo.Name == cachePropertyInfo.Name)
{
var fieldExpressoin = Expression.Field(Expression.Constant(entity), entityPropertyInfo.Name);
memberBindings.Add(Expression.Bind(cachePropertyInfo, fieldExpressoin));
}
}
}
var parameterExpression = Expression.Parameter(typeof(TEntity), "x");
var newExpr = Expression.New(typeof(TCacheItem));
body = Expression.MemberInit(newExpr, memberBindings);
return Expression.Lambda<Func<TEntity, TCacheItem>>(body, parameterExpression);
}
Of course, the #Aravol's answer can make sense, but it is a little different which required in OP. Here is the solution which is more suitable to OP requirement.
public Expression<Func<TEntity, TCacheItem>> BuildSelector<TEntity, TCacheItem>()
{
Type type = typeof(TEntity);
Type typeDto = typeof(TCacheItem);
var ctor = Expression.New(typeDto);
ParameterExpression parameter = Expression.Parameter(type, "p");
var propertiesDto = typeDto.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var memberAssignments = propertiesDto.Select(p =>
{
PropertyInfo propertyInfo = type.GetProperty(p.Name, BindingFlags.Public | BindingFlags.Instance);
MemberExpression memberExpression = Expression.Property(parameter, propertyInfo);
return Expression.Bind(p, memberExpression);
});
var memberInit = Expression.MemberInit(ctor, memberAssignments);
return Expression.Lambda<Func<TEntity, TCacheItem>>(memberInit, parameter);
}
Your best bet is to get very comfortable with the System.Linq.Expressions namespace, which contains all of the methods you'll need to dynamically metacode your method calls and compile them into delegates. See especially Expression.Call and Lambda.Compile methods. Note that using Lambda.Compile, you can also have a true, compiled Delegate, instead of an expression tree (Expression) wrapping the call to your desired method. (NOTE: You can also forgo the Compile step if you really want that expression tree for later)
As for building your set, that's Assembly scanning, and is going to be a matter of iterating over all classes in your Assembly. I highly recommend you utilize, at the very least, a custom Attribute on your assembly or future assemblies to mark them for this scan, lest this process end up much more costly. At the most, you should consider using a custom Attribute to mark which properties you want scanned for this expression build.
the actual code to this tends to start with
AppDomain.CurrentDomain // Necessary to get all available Assemblies
.GetAssemblies() // Gets all the assemblies currently loaded in memory that this code can work with
.AsParallel() // Highly recommended to make the attribute-checking steps run asynchronously
// Also gives you a handy .ForAll Method at the end
// TODO: .Where Assembly contains Attribute
.SelectMany(assembly => assembly.GetTypes())
// TODO: .Where Type contains Attribute
.SelectMany(type => type.GetProperties)
// TODO: Make sure Property has the right data...
.Select(CompileFromProperty)
Where CompileFromProperty is a method taking PropertyInfo and returning the desired Expression.
Look into ToList() and ToDictionary after that, as you may need to break out of the parallelization once you start pushing values to your cache
Addendum: you also have .MakeGenericType on the Type class, which will allow you to specify Generic parameters from other Type variables, which will prove invaluable when building the Expressions. Don't forget about Contravariance when you define the generic types!

How do you call a generic method with out parameters by reflection?

Suppose I have a class like this, containing a generic method with an out parameter:
public class C
{
public static void M<T>(IEnumerable<T> sequence, out T result)
{
Console.WriteLine("Test");
result = default(T);
}
}
From reading the answers to a couple of other questions (How to use reflection to call generic Method? and Reflection on a static overloaded method using an out parameter), I thought I might be able to invoke the method via reflection as follows:
// get the method
var types = new[] { typeof(IEnumerable<int>), typeof(int).MakeByRefType() };
MethodInfo mi = typeof(C).GetMethod(
"M", BindingFlags.Static, Type.DefaultBinder, types, null);
// convert it to a generic method
MethodInfo generic = mi.MakeGenericMethod(new[] { typeof(int) });
// call it
var parameters = new object[] { new[] { 1 }, null };
generic.Invoke(null, parameters);
But mi is coming back null. I've tried using object instead of int in the types array but that doesn't work either.
How can I specify the types (needed for the out parameter) for a generic method before the call to MakeGenericMethod?
This will let you call the method:
MethodInfo mi = typeof(C).GetMethod("M");
MethodInfo generic = mi.MakeGenericMethod(new[] { typeof(int) });
var parameters = new object[] { new[]{1},null};
generic.Invoke(null, parameters);
And to get the out parameter:
Console.WriteLine((int)parameters[1]); //will get you 0(default(int)).
I'm still interested to know what the syntax is for specifying an array of template types, or if it's not possible
I don't think it's possible to pass that kind of detailed type specification to GetMethod[s]. I think if you have a number of such Ms to look through, you have to get them all and then filter by the various properties of the MethodInfos and contained objects, eg as much of this as is necessary in your particular case:
var myMethodM =
// Get all the M methods
from mi in typeof(C).GetMethods()
where mi.Name == "M"
// that are generic with one type parameter
where mi.IsGenericMethod
where mi.GetGenericArguments().Length == 1
let methodTypeParameter = mi.GetGenericArguments()[0]
// that have two formal parameters
let ps = mi.GetParameters()
where ps.Length == 2
// the first of which is IEnumerable<the method type parameter>
where ps[0].ParameterType.IsGenericType
where ps[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
where ps[0].ParameterType.GetGenericArguments()[0] == methodTypeParameter
// the second of which is ref <the method type parameter>
where ps[1].ParameterType.IsByRef
where ps[1].ParameterType.GetElementType() == methodTypeParameter
select mi;
You've passed parameters that will find M<T>(IEnumerable<int>, ref int).
You need to find M(IEnumerable<T>, ref T) (the distinction between ref and out exists only in the C# language; reflection only has ref).
I'm not sure how to pass that; you may need to loop through all methods to find it.
On an unrelated note, you need to pass more BindingFlags:
BindingFlags.Public | BindingFlags.Static
This is a well known-problem; to find the method, you need to know its type parameter, but you can't know its type parameter without knowing the method first...
An obvious but inelegant solution is to loop through all methods until you find the right one.
Another option is to take advantage of the Linq Expression API:
public static MethodInfo GetMethod(Expression<Action> expr)
{
var methodCall = expr.Body as MethodCallExpression;
if (methodCall == null)
throw new ArgumentException("Expression body must be a method call expression");
return methodCall.Method;
}
...
int dummy;
MethodInfo mi = GetMethod(() => C.M<int>(null, out dummy));

How to create a Expression.Lambda when a type is not known until runtime?

This is best explained using code. I have a generic class that has a method that returns an integer. Here is a simple version for the purposes of explaining...
public class Gen<T>
{
public int DoSomething(T instance)
{
// Real code does something more interesting!
return 1;
}
}
At runtime I use reflection to discover the type of something and then want to create an instance of my Gen class for that specific type. That is easy enough and done like this...
Type fieldType = // This is the type I have discovered
Type genericType = typeof(Gen<>).MakeGenericType(fieldType);
object genericInstance = Activator.CreateInstance(genericType);
I now want to create an Expression that will take as a parameter an instance of the generic type and then calls the DoSomething method of that type. So I want the Expression to effectively perform this...
int answer = genericInstance.DoSomething(instance);
...except I do not have the 'instance' until some point later at runtime and the genericInstance is the generated type as can be seen above. My attempt at creating the Lambda for this is as follows...
MethodInfo mi = genericType.GetMethod("DoSomething",
BindingFlags.Instance | BindingFlags.Public);
var p1 = Expression.Parameter(genericType, "generic");
var p2 = Expression.Parameter(fieldType, "instance");
var x = Expression.Lambda<Func<genericType, fieldType, int>>
(Expression.Call(p1, mi, p2),
new[] { p1, p2 }).Compile();
...so that later on I can call it with something like this...
int answer = x(genericInstance, instance);
Of course, you cannot provide Func with instance parameters and so I have no idea how to parameterize the Lambda generation. Any ideas?
I think you would just use the Expression.Lambda that takes the delegate type as a type rather then as a generic, and create your Func on the fly like you are with Gen<>:
MethodInfo mi = genericType.GetMethod("DoSomething",
BindingFlags.Instance | BindingFlags.Public);
var p1 = Expression.Parameter(genericType, "generic");
var p2 = Expression.Parameter(fieldType, "instance");
var func = typeof (Func<,,>);
var genericFunc = func.MakeGenericType(genericType, fieldType, typeof(int));
var x = Expression.Lambda(genericFunc, Expression.Call(p1, mi, p2),
new[] { p1, p2 }).Compile();
This will return a Delegate rather than a strongly typed Func, but you can of course cast it if needed (and seemingly difficult if you don't know what you are casting to), or dynamically invoke it using DynamicInvoke on it.
int answer = (int) x.DynamicInvoke(genericInstance, instance);
EDIT:
A good idea that does indeed work. Unfortunately the reason I want to use a strongly typed compiled Lambda is performance. Using DynamicInvoke is prettty slow compared to a typed Lambda.
This seems to work without the need of a dynamic invoke.
var p1 = Expression.Parameter(genericType, "generic");
var p2 = Expression.Parameter(fieldType, "instance");
var func = typeof(Func<,,>);
var genericFunc = func.MakeGenericType(genericType, fieldType, typeof(int));
var x = Expression.Lambda(genericFunc, Expression.Call(p1, mi, p2), new[] { p1, p2 });
var invoke = Expression.Invoke(x, Expression.Constant(genericInstance), Expression.Constant(instance));
var answer = Expression.Lambda<Func<int>>(invoke).Compile()();
EDIT 2:
A greatly simplified version:
Type fieldType = ;// This is the type I have discovered
Type genericType = typeof(Gen<>).MakeGenericType(fieldType);
object genericInstance = Activator.CreateInstance(genericType);
MethodInfo mi = genericType.GetMethod("DoSomething",
BindingFlags.Instance | BindingFlags.Public);
var value = Expression.Constant(instance, fieldType);
var lambda = Expression.Lambda<Func<int>>(Expression.Call(Expression.Constant(genericInstance), mi, value));
var answer = lambda.Compile()();
This answer only applies if you are using .NET 4.0.
If you make genericInstance dynamic instead of object, you can then call the DoSomething method on it directly, and the dynamic language runtime will take care of everything for you.
class Type1 {
public int DoSomething() { return 1; }
}
class Type2 {
public int DoSomething() { return 2; }
}
static void TestDynamic() {
dynamic t1 = Activator.CreateInstance(typeof(Type1));
int answer1 = t1.DoSomething(); // returns 1
dynamic t2 = Activator.CreateInstance(typeof(Type2));
int answer2 = t2.DoSomething(); // returns 2
}
If you need to keep this class structure (Gen<T>), then I don't see an easy way around the fact that you don't know the type T at compile time. If you want to call the delegate, you either have to know its full type at compile time, or you need to pass in the parameters as objects.
Using dynamic gets you to hide the complexity of getting the MethodInfo, etc., and gives you excellent performance. The one drawback vs. DynamicInvoke that I see is that I believe you get the initial overhead of resolving the dynamic call once for every call site. The bindings are cached so that they run very fast from the second time onwards if you call it on objects with the same type.
It's better to to accept an object and use convert to a known type.
Here is an example, how to build access to a property by name on unknown depth:
var model = new { A = new { B = 10L } };
string prop = "A.B";
var parameter = Expression.Parameter(typeof(object));
Func<object, long> expr = (Func<object, long>) Expression.Lambda(prop.Split('.').Aggregate<string, Expression>(Expression.Convert(parameter, model.GetType()), Expression.Property), parameter).Compile();
expr(model).Dump();
It avoids extra costs of DynamicInvoke when type of delegate is unknown at compile time.

Create delegate type in runtime

I try create delegate type using an Expression class, but when I try create delegate from instance of MethodInfo I've got an ArgumentException. I using .NET 4.0
Here code:
var method = /*...*/;
List<Type> tArgs = new List<Type> { method.ReturnType };
var mparams = method.GetParameters();
mparams.ToList().ForEach(p => tArgs.Add(p.ParameterType));
var delDecltype = Expression.GetDelegateType(tArgs.ToArray());
return Delegate.CreateDelegate(delDecltype, method);
P.S. Sorry for my bad english;)
If you read the documentation for Expression.GetDelegateType(), you would see that the return type has to be the last argument.
That means this code should work:
var tArgs = new List<Type>();
foreach (var param in method.GetParameters())
tArgs.Add(param.ParameterType);
tArgs.Add(method.ReturnType);
var delDecltype = Expression.GetDelegateType(tArgs.ToArray());
return Delegate.CreateDelegate(delDecltype, method);
This code works for static methods only though. If you want create a delegate from instance method, you need to provide the instance you want to call the method on. To do that, change the last line to:
return Delegate.CreateDelegate(delDecltype, instance, method);

Categories