I have a IEnumerable<Person> for example.
I want to be able to make it List<Person> at runtime
I have the below code but errors saying I can't convert System Runtime type
Is there something I'm missing?
private static readonly MethodInfo enumerableToListMethod = typeof(Enumerable).GetMethod("ToList", BindingFlags.Public | BindingFlags.Static);
//genericType will be Person
var genericType = modelType.GetGenericArguments().First();
var genericToListMethod = enumerableToListMethod.MakeGenericMethod(new[] { genericType });
//modelType is IEnumerable<Person>
var ienumtype = genericToListMethod.Invoke(null, new object[] { modelType });
Activator.CreateInstance(ienumtype.GetType());
You've gotta pass in the IEnumerable to the invoke, NOT the type of the enumerable - also, calling invoke will return the List, not a Type.
put in code:
var instance = < the IEnumerable >;
var modelType = instance.GetType();
//modelType is IEnumerable<Person>
var enumerableAsList = genericToListMethod.Invoke(null, new object[] { instance});
Based on your comments, I'm guessing you're after something more like this:
Func<IEnumerable<T>, List<T>> MakeMeAToListerForEnumerablesOfType<T>()
{
var itemType = typeof(T);
var enumerableToListMethod = typeof(Enumerable).GetMethod("ToList", BindingFlags.Public | BindingFlags.Static);
var genericToListMethod = enumerableToListMethod.MakeGenericMethod(new[] { itemType });
return (Func<IEnumerable<T>, List<T>>)Delegate.CreateDelegate(typeof(Func<IEnumerable<T>, List<T>>), genericToListMethod);
}
Which you would call like:
IEnumerable<string> enumerable = new string[] { "a", "b", "c" };
var toLister = MakeMeAToListerForEnumerablesOfType<string>();
Derp. Missed the call...
var enumerableAsList = toLister(enumerable);
Related
I have a class implementation that inherits from AbstractValidator<T>
to keep things simple: Assume I have
public class Users{
[Required]
public string Name {get;set;}
}
what I woul like to do is to invoke
RuleFor(x => x.Name).NotEmpty();
but the properties will be know at runtime so I would like to do this with reflection and expression trees..
I'm failing to invoke RuleFor method, below is my implementation and the commented parts are things I've tried.. any help is much appreciated.
GenValidator<T> : AbstractValidator<T>
{
// constructor etc..
public async Task<IEnumerable<string>> ValidateValueAsync(T model, string propertyName)
{
try
{
var loType = model.GetType();
var property = loType.GetProperty(propertyName);
var param = Expression.Parameter(loType);
var propertyExpression = Expression.Property(param, property);
var lambdaExpressionType = typeof(Expression);
var lambda = Expression.Lambda(typeof(Func<,>).MakeGenericType(model.GetType(), typeof(object)), propertyExpression, param);
var lambdaMethod = typeof(Expression).GetMethod("Lambda", new Type[] { typeof(Type), typeof(Expression), typeof(ParameterExpression[]) });
var lambda2 = lambdaMethod.MakeGenericMethod(loType, typeof(object)).Invoke(null, new object[] { propertyExpression, new ParameterExpression[] { param } });
var compileMethod = lambda.GetType().GetMethod("Compile");
var func = compileMethod.Invoke(lambda2, null);
var ruleForMethod = typeof(AbstractValidator<T>).GetMethod("RuleFor");
var genericRuleForMethod = ruleForMethod.MakeGenericMethod(loType);
genericRuleForMethod.Invoke(this, new object[] { func });
//RuleFor<object>(lambda).NotEmpty().WithMessage("The property is required.");
//RuleFor(lambda.Compile()).NotEmpty().WithMessage("The property is required.");
}
catch (Exception ex)
{
}
//var lambda = Expression.Lambda<Func<T, object>>(property, param);
//RuleFor(lambda).NotEmpty().WithMessage("The property is required.");
var valContext = ValidationContext<T>.CreateWithOptions(model, x => x.IncludeProperties(_propertyName));
var result = await ValidateAsync(valContext);
if (result.IsValid)
{
return Array.Empty<string>();
}
return result.Errors.Select(e => e.ErrorMessage);
}
}
EDIT
public class MyValidator
{
public MyValidator(string propertyname)
{
// this is how you call using fluentvalidation but it's hardcoded.
RuleFor(x => x.Name).NotEmpty();
// I do know want to hard code.
//I want to do some magical stuff at this point
//and invoke the RuleFor method, I only have propertyname as a
//string, type of model and model at this point
}
}
EDIT2
private void GenericRuleFor(T Model, string propertyName)
{
var property = Model.GetType().GetProperty(propertyName);
var param = Expression.Parameter(Model.GetType());
var propertyExpression = Expression.Property(param, property);
var lambda = Expression.Lambda(typeof(Func<,>).MakeGenericType(Model.GetType(), property.PropertyType), propertyExpression, param);
var abstractValidatorType = typeof(AbstractValidator<>).MakeGenericType(Model.GetType());
var ruleForMethod = abstractValidatorType.GetMethods().First(m => m.Name == "RuleFor" && m.IsGenericMethodDefinition);
var genericRuleForMethod = ruleForMethod.MakeGenericMethod(property.PropertyType);
genericRuleForMethod.Invoke(this, new object[] { lambda });
}
this is the modified version of my code which still does not work..
it throws an exception when I invoke genericRuleForMethod
telling me that object type does not match Target type
Here is my working version of your GenericRuleFor is below. The main difference seems like you are trying to get the RuleFor method from the typeof(AbstractValidator<>) I just get it from the this.GetType()
private void GenericRuleFor(string propertyName)
{
var type = typeof(T);
var property = type.GetProperty(propertyName);
var param = Expression.Parameter(type);
var propertyExpression = Expression.Property(param, property);
var lambda = Expression.Lambda(typeof(Func<, >).MakeGenericType(type, property.PropertyType), propertyExpression, param);
var thisType = this.GetType();
var ruleForMethod = thisType.GetMethod("RuleFor", BindingFlags.Public | BindingFlags.Instance);
var genericRuleForMethod = ruleForMethod.MakeGenericMethod(property.PropertyType);
// result is used by extension method
var result = genericRuleForMethod.Invoke(this, new object[]{lambda});
//NotEmpty method is an Extension metot which is contained by DefaultValidatorExtensions
var extensionsType = typeof(DefaultValidatorExtensions);
var notEmptyMethod = extensionsType.GetMethod("NotEmpty", BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(type, property.PropertyType);
notEmptyMethod.Invoke(null, new object[]{result});
}
Here is the fiddle
I want to get a list of each object from my List<T> (except strings, ints etc). And then Invoke (generic, recursive method with reflection). The problem is I am iterating on the property names, and have no idea how to select.
Error CS0021 Cannot apply indexing with [] to an expression of type 'T'
Code:
public static void My method<T>(IEnumerable<T> query)
{
var t = typeof(T);
var Headings = t.GetProperties();
for (int i = iteratorStart; i < Headings.Count(); i++)
{
if (IsValue(Headings[i].PropertyType.FullName))
{
}
else
{
Type type = Type.GetType(Headings[i].PropertyType.FullName);
var mi = typeof(ExcelExtension);
var met = mi.GetMethod("ListToExcel");
var genMet = met.MakeGenericMethod(type);
var nested = query.Select(p => p[Headings[i].Name]);
object[] parametersArray = new object[] { pck, nested, i };
genMet.Invoke(null, parametersArray);
}
}
}
As far as I can see, this is what you want:
public static void Mymethod<T>(IEnumerable<T> query)
{
var t = typeof(T);
int pck = 1234;
var mi = typeof(ExcelExtension);
var met = mi.GetMethod("ListToExcel");
var Headings = t.GetProperties();
for(int i=0; i < Headings.Length; ++i)
{
var prop = Headings[i];
if (prop.PropertyType.IsClass)
{
var genMet = met.MakeGenericMethod(prop.PropertyType);
var nested = query.Select(p => prop.GetValue(p));
object[] parametersArray = new object[] { pck, nested, i };
genMet.Invoke(null, parametersArray);
}
}
}
class ExcelExtension
{
public void ListToExcel<T>(int pck, IEnumerable<object> nested, int i)
{
}
}
Assuming you are using c# 6.0 or higher. You can use generic type parameters like;
public static void MyMethod<T>(IEnumerable<T> query) where T : IList
{
//Your code here
}
This way, you ensure that T is List of something and reaching indexing won't be a problem.
UPDATE
I misunderstood the question earlier. Here is the updated solution.
public static void MyMethod<T>(IEnumerable<T> query)
{
var t = typeof(T);
var Headings = t.GetProperties();
for (int i = iteratorStart; i < Headings.Count(); i++)
{
if (false == IsValue(Headings[i].PropertyType.FullName))
{
Type type = Type.GetType(Headings[i].PropertyType.FullName);
var mi = typeof(ExcelExtension);
var met = mi.GetMethod("ListToExcel");
var genMet = met.MakeGenericMethod(type);
//Assuming you want to get property value here. IF not You can use like Headings[i].GetName
var nested = query.Select(p =>Convert.ChangeType( Headings[i].GetValue(p),Headings[i].GetType()));
object[] parametersArray = new object[] { pck, nested, i };
genMet.Invoke(null, parametersArray);
}
}
}
Error Explanation:
The problem is in the Select(p => p[something here]) part. Since p is not the property list or array but a type of object, it doesn't contain any indexer. You should use reflection like above example.
static class Extensions
{
public static string Primary<T>(this T obj)
{
Debug.Log(obj.ToString());
return "";
}
public static string List<T>(this List<T> obj)
{
Debug.Log(obj.ToString());
return "";
}
}
Use reflection to invoke the two extension methods
//This works
var pmi = typeof(Extensions).GetMethod("Primary");
var pgenerci = pmi.MakeGenericMethod(typeof(string));
pgenerci.Invoke(null, new object[] {"string" });
//This throw a "ArgumentException: failed to convert parameters"
var mi = typeof(Extensions).GetMethod("List");
var stringGeneric = mi.MakeGenericMethod(typeof(List<string>));
stringGeneric.Invoke(null, new object[] {new List<string> { "list of string"}, });
I'm working with Unity3d, so the .net version is 3.5
The type that you need to pass to MakeGenericMethod is string, not List<string>, because the parameter is used as T.
var mi = typeof(Extensions).GetMethod("List");
var stringGeneric = mi.MakeGenericMethod(typeof(string));
stringGeneric.Invoke(null, new object[] {new List<string> { "list of string"} });
Otherwise, you are making a method that accepts a list of lists of strings.
Because typeof(List<"T">) did not return the right type.
You should write an extension method to get the type of generic list.
or you can modify your code like this
var listItem = new List<string> { "alex", "aa" };
var typeOfGeneric = listItem.GetType().GetGenericArguments().First<Type>();
var mi = typeof(Extensions).GetMethod("List");
var stringGeneric = mi.MakeGenericMethod(typeOfGeneric);
stringGeneric.Invoke(null, new object[] { listItem });
=> it works
Im trying to create a Proxy Class from an interface. In the methods I just want to collect all arguments in an object array and pass on to a known method. So faar I have managed to get it working without params and with return types. As soon as I try to create my array I get "Additional information: Common Language Runtime detected an invalid program.".. Dont really know how to debug from here and the IL codes seems to be correct ( ? ).
public class Program
{
static void Main(string[] args)
{
var v = CreateProxy<IFoo>();
v.DoSomething();
}
public static void TheMethod(object[] args)
{
}
public interface IFoo
{
void DoSomething();
}
public static T CreateProxy<T>()
{
var interfaceType = typeof(T);
AssemblyName assemblyName = new AssemblyName(string.Format("tmp_{0}", interfaceType.FullName));
string moduleName = string.Format("{0}.dll", assemblyName.Name);
string ns = interfaceType.Namespace;
if (!string.IsNullOrEmpty(ns))
ns += ".";
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName,AssemblyBuilderAccess.RunAndSave);
var module = assembly.DefineDynamicModule(moduleName, false);
var type = module.DefineType(String.Format("{0}Proxy_{1}", ns, interfaceType.Name), TypeAttributes.Class | TypeAttributes.AnsiClass |TypeAttributes.Sealed |TypeAttributes.NotPublic);
type.AddInterfaceImplementation(interfaceType);
//Constructor
var ctor = type.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[] {});
var generator = ctor.GetILGenerator();
generator.Emit(OpCodes.Ret);
//Methods
foreach (var method in interfaceType.GetMethods())
{
var args = method.GetParameters();
var methodImpl = type.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.Virtual, method.ReturnType, (from arg in args select arg.ParameterType).ToArray());
generator = methodImpl.GetILGenerator();
generator.Emit(OpCodes.Nop);
generator.Emit(OpCodes.Ldc_I4_1);
generator.Emit(OpCodes.Newarr, typeof(object));
generator.Emit(OpCodes.Stloc_0);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Call, typeof(Program).GetMethod(nameof(Program.TheMethod)));
generator.Emit(OpCodes.Nop);
generator.Emit(OpCodes.Ret);
}
return (T)Activator.CreateInstance(type.CreateType());
}
}
The method im trying to Emit should look like this.
public void DoSomething()
{
object[] arr = new object[1];
Program.TheMethod(arr);
}
What am I missing here ?
You should initialize locals:
foreach (var method in interfaceType.GetMethods())
{
var args = method.GetParameters();
var methodImpl = type.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.Virtual, method.ReturnType, (from arg in args select arg.ParameterType).ToArray());
generator = methodImpl.GetILGenerator();
generator.DeclareLocal(typeof(object[]));
....
....
I have a generic Class
public class MyClass<T>
{
public List<T> Translate(List<T> list, params string[] names)
{
//do something here,modify list and return list
}
}
Now i can easily create its instances like
MyClass<Employee> obj= new MyClass<Employee>(); OR
MyClass<Vehicle> obj = new MyClass<Vehicle>();
I can call my method like
obj.Translate(Mylist of employee or vehicle type,"param1","param2")
But in my case i dont know the type T as its generated on runtime,see the code below
String classname = string.Empty;
if(Classtype == 1)
{
classname = "Employee"
}
else if(classtype == 2)
{
classname = "Vehicle"
}
I want something like below..so that i can creat an instance of this generic class
MyClass<typeof(classname)> empp = new MyClass<typeof(classname)>();
empp.Translate(MyList,"param1","param2")
Please suggest,how can i do that.
Try
var someType = Type.GetType("MyProject.Employee");
var yourGenericType = typeof(MyClass<>).MakeGenericType(new [] { someType });
var instance = Activator.CreateInstance(yourGenericType);
Note that Type.GetType(...) only works with the full namespace of a type, or even the full assembly qualified name if your class is not in the same dll as the code that is executing this.
Thanks All for the Help I was able to do it by this way
var someType = Type.GetType("MyProject.Employee");
var genericType = typeof(Convert<>).MakeGenericType(new[] { someType });
var instance = Activator.CreateInstance(genericType);
string instanceType = instance.GetType().FullName;
MethodInfo methodInfo = Type.GetType(instanceType).GetMethod("Translate");
genericType.InvokeMember(methodInfo.Name, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, instance, new object[] { lstEmp, "Name", "City" });
ishan
public T CreateEmptyObject<T>()
{
return (T)Activator.CreateInstance(typeof(T));;
}
To call it:
YourClassObject yourClassObject = CreateEmptyObject<YourClassObject>();