It's there a way to invoke a method and the return type is strong typed ?
There is an example of code
public static IQueryable<T> FilterVersion<T>(this Table<T> t, IVersionIndexFilter version)
where T : class
{
try
{
// Define the new type of my table
Type versionTableType = Type.GetType(typeof(T).AssemblyQualifiedName.Replace(typeof(T).FullName, string.Concat(typeof(T).FullName, VersionIndexTableExtensionName)));
// Get the method info generic with a custom helper
var getTableType = MethodInfoHelper.GetGenericMethod(typeof(DataContext), "GetTable", new Type[] { versionTableType }, new Type[] { }, typeof(Table<>).MakeGenericType(typeof(Table<>).GetGenericArguments()[0]), BindingFlags.Public | BindingFlags.Instance);
// Get the object with a invoke but invoke return object, i need he return a Table<vesrionTableType>
var joinTable = getTableType.Invoke(t.Context, null);
// Put my new object in anonymous class
var result = new { FromT = t, InnerT = joinTable };
// Write the type of the property and is {System.Object InnerT}
Console.Write(result.GetType().GetProperty("InnerT"));
}
I need my Console.Write(result.GetType().GetProperty("InnerT")); return a Table<versionTableType>
It's there a way i cand do that ? Any suggestion ?
There is my GetGenericMethod
public static MethodInfo GetGenericMethod(Type t, string name, Type[] genericArgTypes, Type[] argTypes, Type returnType, BindingFlags flags)
{
if (genericArgTypes == null)
{
genericArgTypes = new Type[] { };
}
MethodInfo genericMethod = (from m in t.GetMethods(flags)
where m.Name == name
&& m.GetGenericArguments().Length == genericArgTypes.Length
&& m.GetParameters().Select(pi => pi.ParameterType.IsGenericType ? pi.ParameterType.GetGenericTypeDefinition() : pi.ParameterType).SequenceEqual(argTypes) &&
(returnType == null || (m.ReturnType.IsGenericType ? m.ReturnType.GetGenericTypeDefinition() : m.ReturnType) == returnType)
select m).FirstOrDefault();
if (genericMethod != null)
{
return genericMethod.MakeGenericMethod(genericArgTypes);
}
return null;
}
I get my method info generic correctly. The probleme is when i assign to another property the result of my invoke is an object. I need to strong type my result
EDIT : It's a good idea the static method CreateResult but not enough ... there is what i try to do after with my value
NewExpression exResult = Expression.New(result.GetType().GetConstructor(new Type[] { t.GetType(), typeof(Table<>).MakeGenericType(versionTableType) }), new List<Expression>() { outer, inner }, result.GetType().GetProperty("FromT"), result.GetType().GetProperty("InnerT"));
There is the error i got after try the technique
Argument type 'System.Data.Linq.Table`1[Nms.Media.Business.Manager.Data.Linq.ImageLibraryItemBinaryLocaleVersionIndex]' does not match the corresponding member type 'System.Object'
Thanks
EDIT: Okay, since your question edit it looks like you're already calling MakeGenericMethod. Good.
Now, once you've managed to invoke the method, there's then the matter of making an appropriate anonymous type. I would suggest you create a generic type and use Type.MakeGenericType on the generic type definition, then construct an instance of that. Alternatively, you could have another generic method which is strongly typed, and invoke that with reflection in a similar way. That method could use an anonymous type:
public object CreateResult<TTable, TJoin>(TTable t, TJoin joinTable)
{
return new { FromT = t, InnerT = joinTable };
}
You could call your generic MethodInfo helper to make it easier to invoke this.
I Auto resolve my solutions, inspired from Jon Skeet answer.
I create a class generic
public class ConvertTable<TOuter, TInner>
where TInner : class
where TOuter : class
{
public TOuter FromT { get; set; }
public TInner InnerT { get; set; }
public ConvertTable(TOuter fromT, TInner innerT)
{
FromT = fromT;
InnerT = innerT;
}
}
Related
I have an interface
public interface IBsonClassMap<T>
where T : class
{
void Configure(BsonClassMap<T> map);
}
which serves as base for all mappings for mongo collections.
An implementation of it looks like this
public class StudentClassMap : IBsonClassMap<Student>
{
void IBsonClassMap<Student>.Configure(BsonClassMap<Student> map)
{
}
}
I'm using an extension method to scan an assembly and invoke each mapping found.
This is it.
public static void ApplyConfigurationFromAssemblies(this IServiceCollection services, params Assembly[] assemblies)
{
Type _unboundGeneric = typeof(IBsonClassMap<>);
List<(Type Type, Type Handler, Type Argument)> types = new List<(Type, Type, Type)>();
foreach (Assembly assembly in assemblies)
{
types.AddRange(assembly
.GetExportedTypes()
.Where(type =>
{
bool implementsType = type.GetInterfaces().Any(#interface => #interface.IsGenericType && #interface.GetGenericTypeDefinition() == _unboundGeneric);
return !type.IsInterface && !type.IsAbstract && implementsType;
})
.Select(type =>
{
Type #inteface = type.GetInterfaces().SingleOrDefault(type => type.GetGenericTypeDefinition() == _unboundGeneric);
Type argument = #inteface.GetGenericArguments()[0];
return (type, #inteface, argument);
}));
}
types.ForEach(type =>
{
object classMapInstance = Activator.CreateInstance(type.Type);
Type unboundGeneric = typeof(BsonClassMap<>);
Type boundedGeneric = unboundGeneric.MakeGenericType(type.Argument);
type.Handler.GetMethod("Configure").Invoke(classMapInstance, new object[] { boundedGeneric });
});
}
The issue is taht I'm getting
Object of type 'System.RuntimeType' cannot be converted to type
'MongoDB.Bson.Serialization.BsonClassMap`1[Platform.Concepts.Mongo.Collections.Student]'.
Also, everything works as expected if I'm removing the argument in the Configure method of the IBsonClassMap, and addapt everhting accordingly. The method ends up getting invoked.
So instead of this
type.Handler.GetMethod("Configure").Invoke(classMapInstance, new object[] { boundedGeneric });
I have this
type.Handler.GetMethod("Configure").Invoke(classMapInstance, null);
You are passing a Type into a method that expects a concrete class of BsonClassMap<T>
It seems you want
object classMapInstance = Activator.CreateInstance(type.Type);
Type unboundGeneric = typeof(BsonClassMap<>);
Type boundedGeneric = unboundGeneric.MakeGenericType(type.Argument);
// create the generic instance
object o = Activator.CreateInstance(boundedGeneric);
type.Handler.GetMethod("Configure").Invoke(classMapInstance, new object[] { o });
Note : Completely untested, and based entirely on my spidey senses
I'm trying to call a generic method at run time, and have working code. However, I was wondering if there was a better way of getting the method info, as should i change the method name, it will break.
public int Handle<T>(CreateCodeModel obj) where T : CodeModel
{
//Create code here...
}
//codeType is selected by user.
public int Handle(CreateCodeModel obj, Type codeType)
{
MethodInfo method = this.GetType().GetMethod("Handle");
//MethodInfo method = this.GetType().GetMethods()
.Where(mi => mi.IsGenericMethod && mi.Name == "Handle").First();
MethodInfo genericMethod = method.MakeGenericMethod(new Type[] { codeType });
return (int)genericMethod.Invoke(this, new object[] { obj });
}
I had hoped there was a nicer way of doing this, perhaps using actions to get the method info, but then I still need to provide a type, e.g.
Action<CreateCodeModel> h = (x) => Handle(x);
MethodInfo method = h.Method;
You are on the right track here. Your delegate signature must match the method you are calling, so in this case that would be Func<CreateCodeModel,int> instead of Action<CreateCodeModel>. And you must supply a generic parameter that matches the generic constraints. This could be anything since we'll just remove it with our call to 'GetGenericMethodDefinition', but I like to use the class from the constraint.
The other piece of the code below is the idea that you only need to get the generic method definition once, as it doesn't change, so I store that in a static variable so it can be reused.
public int Handle<T>(CreateCodeModel obj) where T : CodeModel
{
//Create code here...
return 7;
}
// this static variable preserves the generic MethodInfo, so we don't have
// keep discovering it with reflection
private static MethodInfo _methodInfoForHandle;
// The generic MethodInfo only needs to be discovered the first time this runs
private MethodInfo MethodInfoForHandle
{
get
{
return _methodInfoForHandle ?? (_methodInfoForHandle = GetMethodInfoForHandleMethod());
}
}
private MethodInfo GetMethodInfoForHandleMethod()
{
Func<CreateCodeModel, int> handleFunc = Handle<CodeModel>;
return handleFunc.Method.GetGenericMethodDefinition();
}
//codeType is selected by user.
public int Handle(CreateCodeModel obj, Type codeType)
{
MethodInfo genericMethod = MethodInfoForHandle.MakeGenericMethod(new Type[] { codeType });
return (int)genericMethod.Invoke(this, new object[] { obj });
}
public class CreateCodeModel { }
public class CodeModel { }
public class JavascriptCodeModel : CodeModel { }
Runtime Mapping objects of type A into a similar type A`
List of types is discovered run-time using System.Reflection
An expression Expression<Func<object, object>> is made for each appropriate Type. The function converts object of type oldType into an object of newType.
Sidenote:
The generic type arguments for the Func<object, object> are somewhat-misleading, it feels like it should be Func<newType,oldType>, but the types are not known compile-time:
Expression<Func<object, object>> GetConvertExpression(Type oldType, Type newType)
The expressions are compiled and stored into a Dictionary<Type, Func<object, object>>
Question - How to solve the downsides of the current approach?
A dictionary lookup is necessary to convert an object. I had thought of baking in a big if/then/else or case statment into a big unified Expression, this could work well with a small number of types.
Unnecessary casting operations within the generated IL.
Argument and return type of the generated code is of type object. This basically ensures that there will be casting operations all around. Especially when a large transformation of List<oldType> to List<newType> has to be performed.
Generated code from the Expression:
object convert(object oldInst)
{
newType newInst = newType();
oldType oldInstCast = (oldType)oldInst;
newInst.field1 = oldInstCast.field1;
.......
// note: if some field of oldInst has a type in the DICTIONARY and
// it's value is not null, we have to perform the convert
// operation recursively for that field
if(oldInstCast.field3 != null)
{
// Type of the field was determined when this code was generated
// And it is known that the dictionary will contain it
Func<object, object> transformFun = dictionary[oldFieldType];
newInst.field3 = (newFieldType)transformFun(oldInstCast.field3);
}
newInst.fieldz = oldInstCast.fieldz;
return newInst;
}
EDIT: I wrote the original answer at around midnight. I think it shows. It sounds like what you need most are how to construct the delegate when the parameter and return types are only known at runtime. You get around this problem by using object. You can still do it strongly typed. The most relevant calls are:
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(A), typeof(NewA));
This creates the delegate type Func<A, NewA> as for converting from A to NewA. To create an Expression<Func<A, NewA>> when A and NewA aren't necessarily known at compile time, you can use this overload of Expression.Lambda.
Previous answer:
Excellent question. I had fun trying to draw up a solution with stronger typing.
I did a mockup of this and I see no reason you can't generate expressions of type Expression<Func<TOld, TNew>> instead of Expression<Func<object, object>>. You wouldn't need casting inside of those conversion functions. However, I'm still not sure it's a better solution. Here's what I came up with so far:
The meat of the conversion/reflection/expression code is in a class I called ConverterDictionary, which builds these converters then holds them in a dictionary, and offers them up in a nicely typed way. I'm guessing that 95% of this is non-material for you since it sounds like you already figured out the reflection and expression-building portions of your problem. Everything else is 100% sample code.
Let me know what needs clarification or further explanation, or what could be more helpful.
class Program
{
static void Main(string[] args)
{
var cv = new ConverterDictionary();
var type2_converter = cv.GetConverter<Type2, Type2_New>();
var converterF = type2_converter.Compile();
var type2 = new Type2 { Field1 = "Hello", Field2 = "World" };
var type2_New = converterF(type2);
}
}
public class ConverterDictionary
{
private Dictionary<Tuple<Type, Type>, object> conversionDict = new Dictionary<Tuple<Type, Type>, object>();
public ConverterDictionary()
{
var convertPairs = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.CustomAttributes.Any(a => a.AttributeType == typeof(ClassConvertAttribute)))
.Select(t => Tuple.Create(t, (Type)(t.CustomAttributes.First().NamedArguments[0].TypedValue.Value)))
.ToList();
foreach(var pair in convertPairs)
{
var fromType = pair.Item1;
var toType = pair.Item2;
var fieldConversions = fromType.GetFields()
.Where(f => f.CustomAttributes.Any(a => a.AttributeType == typeof(FieldConvertAttribute)))
.Select(f => Tuple.Create(f, toType.GetField((string)(f.CustomAttributes.First().NamedArguments[0].TypedValue.Value))))
.ToList();
var delegateType = typeof(Func<,>).MakeGenericType(fromType, toType);
var param1 = Expression.Parameter(fromType, "oldInst");
var returnVar = Expression.Variable(toType, "newInst");
var expr = Expression.Lambda(
delegateType,
Expression.Block(
new ParameterExpression[] { returnVar },
new Expression[]
{
Expression.Assign(
returnVar,
Expression.New(toType)
),
}.Concat(
fieldConversions.Select(fc =>
Expression.Assign(
Expression.MakeMemberAccess(
returnVar,
fc.Item2
),
Expression.MakeMemberAccess(
param1,
fc.Item1
)
)
)
).Concat(
new Expression[] { returnVar }
)
),
param1
);
conversionDict[pair] = expr;
}
}
public Expression<Func<TFrom, TTo>> GetConverter<TFrom, TTo>()
{
var key = Tuple.Create(typeof(TFrom), typeof(TTo));
if (conversionDict.ContainsKey(key))
return conversionDict[key] as Expression<Func<TFrom, TTo>>;
return null;
}
}
[ClassConvert(ToType=typeof(Type1_New))]
public class Type1
{
[FieldConvert(ToFieldName = "Field1")]
public string Field1;
}
[ClassConvert(ToType = typeof(Type2_New))]
public class Type2
{
[FieldConvert(ToFieldName="Field1")]
public string Field1;
[FieldConvert(ToFieldName = "Field2_New")]
public string Field2;
}
public class Type1_New
{
public string Field1;
}
public class Type2_New
{
public string Field1;
public string Field2_New;
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class ClassConvertAttribute : Attribute
{
public Type ToType { get; set; }
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class FieldConvertAttribute : Attribute
{
public string ToFieldName { get; set; }
}
I have a generic class with a static method, that method using the type parameter:
GenericClass<T>
{
public static void Method()
{
//takes info from typeof(T)
}
}
Now, I need to access that static method, but not simply using GenericClass<KnownType>.Method(). I need to do this having a Type instance. So:
public void OutsiderMethod(Type T)
{
GenericClass<T>.Method()
//it's clear this line won't compile, for T is a Type instance
//but i want some way to have access to that static method.
}
Using reflections, I probably can get a way to invoke that method by it's string name, using some MethodInfo stuff.
That is partially good, solves the question.
But if possible, I'd love to have not to use the name as string.
Anyone???
Generic methods of non-generic classes are easier to access than non-generic methods of generic classes.
You can create a helper method that simply calls the real method:
void OutsiderMethodHelper<T>()
{
GenericClass<T>.Method();
}
You can then get the MethodInfo for that method without looking it up by name-as-string:
public void OutsiderMethod(Type T)
{
Action action = OutsiderMethodHelper<object>;
action.Method.GetGenericMethodDefinition().MakeGenericMethod(T).Invoke(null, null);
}
Here is an example using the Expression:
public static class GenericHelper
{
public static object Invoke(Expression<Action> invokeMethod, object target, Type genericType, params object[] parameters)
{
MethodInfo methodInfo = ParseMethodExpression(invokeMethod);
if (!methodInfo.DeclaringType.IsGenericType)
throw new ArgumentException("The method supports only generic types");
Type type = methodInfo.DeclaringType.GetGenericTypeDefinition().MakeGenericType(genericType);
MethodInfo method = type.GetMethod(methodInfo.Name);
return method.Invoke(target, parameters);
}
public static object Invoke(Expression<Action> invokeMethod, Type genericType, params object[] parameters)
{
return Invoke(invokeMethod, null, genericType, parameters: parameters);
}
private static MethodInfo ParseMethodExpression(LambdaExpression expression)
{
Validate.ArgumentNotNull(expression, "expression");
// Get the last element of the include path
var unaryExpression = expression.Body as UnaryExpression;
if (unaryExpression != null)
{
var memberExpression = unaryExpression.Operand as MethodCallExpression;
if (memberExpression != null)
return memberExpression.Method;
}
var expressionBody = expression.Body as MethodCallExpression;
if (expressionBody != null)
return expressionBody.Method;
throw new NotSupportedException("Expession not supported");
}
}
The method call will be look like this:
GenericHelper.Invoke(() => GenericClass<object>.Method(), typeof(string));
I have a method
T Get<T>(string key)
{..
}
If the caller calls me with T = IEnumerable<V> I need to do:
return GetEnum<V>(key)
Thus I need to
test if T is IEnumerable<X>
get X and shove it into the GetEnum
method
I suspect that I cant do the second one
Obviously I can write a different method but thats not my contract with the existing code base.
You can do it with a little reflection, but it won't be particularly fast:
static class TheClass
{
public static T Get<T>(string key)
{
// Adjust these as required:
const BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic;
if (typeof(T).IsGenericType && typeof(IEnumerable<>) == typeof(T).GetGenericTypeDefinition())
{
Type v = typeof(T).GetGenericArguments()[0];
var baseMethod = typeof(TheClass).GetMethod("GetEnum", flags);
var realMethod = baseMethod.MakeGenericMethod(v);
return (T)(object)realMethod.Invoke(null, new[] { key });
}
// TODO: Handle other types...
}
private static IEnumerable<T> GetEnum<T>(string key)
{
// TODO: Return an enumerable...
}
}
EDIT
If you want to check whether the required return type implements IEnumerable<>, you can use:
Type enumerable = typeof(T).GetInterface("System.Collections.Generic.IEnumerable`1");
if (enumerable != null)
{
Type v = enumerable.GetGenericArguments()[0];
var baseMethod = typeof(TheClass).GetMethod("GetEnum", flags);
var realMethod = baseMethod.MakeGenericMethod(v);
return (T)(object)realMethod.Invoke(null, new[] { key });
}
However, your GetEnum<V> method will have to return a value which can be cast to T, otherwise you'll get an invalid cast exception.
For example, if your GetEnum<V> method returns new List<T>(...), then your Get<T> method will only work if T is either List<T> or an interface implemented by List<T>. If you call Get<HashSet<int>>, it will fail.