I have a generic class with a generic method:
public class GenericClass<T> where T : class
{
public void GenericMethod<T>(T item)
{
// do work here
}
}
I also have an object with multiple properties, some of which can be other objects:
public class TestObject
{
public ChildObject ChildObject { get; set; }
}
I am then attempting to use a generic method which will reflect through TestObject and call GenericMethod for all properties that are in and of themselves custom classes such as ChildObject (I do have a way of determining this based on inheritance, however for the sake of keeping it simple did not include that code for this example):
public void ReflectingMethod<T>(T item)
{
var properties = item.GetType().GetProperties();
foreach (var property in properties)
{
var type = property.PropertyType;
dynamic propertyModel = property.GetValue(model, null);
var castedObject = Convert.ChangeType(propertyModel, type);
var genericClass = Activator.CreateInstance(typeof(GenericClass<>).MakeGenericType(type));
var method = genericClass.GetType().GetMethod("GenericMethod", new [] { type });
method.Invoke(castedObject, null);
}
}
The problem is that whether I attempt to change property's type (as shown in the above example) or I pass property directly to method.Invoke, such as:
method.Invoke(propertyModel, null);
I still receive the same error:
Object does not match target type.
At RunTime method is:
GenericMethod(TestProject.ChildObject)
And castedObject is:
TestProject.ChildObject
I am confused as to why I am getting the error I am, when it would appear that the casted object is exactly of the type that the method is looking for.
EDIT 1
Included the call to GetValue that I had originally left out when posting the question.
The call
method.Invoke(castedObject, null);
is incorrect. It should be
method.Invoke(genericClass, new object[] { castedObject });
Since you are trying to call an instance method, the first argument to Invoke(object, object[]) must be the this instance. For static method, pass null as first argument. Method arguments are always passed via the second object[] argument of the Invoke method.
Related
I have a generic class and a property of type Action<,>. I am wondering if there is a way to actually invoke this delegate using reflection in runtime, not just set value to this class property (via PropertyInfo.SetValue).
I tried a lot of things, like using expressions, dummy casting, read forums, but none of the solutions worked for me.
Workaround:
What I could think of is creating a dummy method which internally calls the delegate, and with reflection is fairly easy to invoke this method.
public class Student
{
public string Name { get; set; }
}
public class ConfigData<T>
where T: class
{
public Action<T, object> ValueInjector { get; set; }
public void SetValue(T entity, object valueToSet)
{
this.ValueInjector(entity, valueToSet);
}
}
class Program
{
static void Main(string[] args)
{
var configObj = new ConfigData<Student>()
{
ValueInjector = (x, y) =>
{
// Some custom logic here
x.Name = y.ToString();
}
};
// Parameters
Student student = new Student();
object valueToSet = "Test";
Type configType = configObj.GetType();
PropertyInfo propertyInfo = configType.GetProperty("ValueInjector");
// Invoke the property info somehow with the parameters ?
// Workarround - invoke a dummy method instead
MethodInfo methodInfo = configType.GetMethod("SetValue");
methodInfo.Invoke(configObj, new object[] { student, valueToSet });
Console.WriteLine(student.Name);
}
}
I want to be able to invoke the propertyInfo variable and pass to it the two parameters I already have (student, valueToSet), since I know that it represent a delegate which can be run.
Update:
I tried with castings as suggested by #HimBromBeere.
//Error in runtime
var del = (Action)propertyInfo.GetValue(configObj, null);
//Error in runtime
var del = (Action<object, object>)propertyInfo.GetValue(configObj, null);
// Works but no generic
var del = (Action<Student, object>)propertyInfo.GetValue(configObj, null);
del.Invoke(student, valueToSet);
Only the last casting works and I am able to call Invoke on the delegate (no need of DynamicInvoke) and it works. However this is not a solution because I do not know the exact type to cast in runtime. I have it as variable T. Something like:
var del = (Action<T, object>)propertyInfo.GetValue(configObj, null);
So maybe if I manage to make a generic Type like this:
var d1 = typeof(Action<,>);
Type[] typeArgs = { propertyInfo.DeclaringType.GenericTypeArguments[0], typeof(object) };
Type delegateType = d1.MakeGenericType(typeArgs);
there might be a way to do this conversion and execute. Still wondering.
You can cast the value returned from the property back to a delegate, e.g:
var del = (Action)propertyInfo.GetValue(configObj, null);
Now call the delegate with your params:
del.DynamicInvoke(student, valueToset)
I have a generic class that has a generic method that uses the same type as the type passed when instantiating the object. At runtime, I will only know the name of the object I need to pass in by a string representation of that object name. I've read a few things about using Activator and possibly using dynamic but I can't wrap my head around how I need to make this work. Here's a snippet of what my generic class looks like:
public class MyClass<T> where T : class, new()
{
public IList<T> MyMethod(Stream stream)
{
var data = new List<T>();
// stuff to create my list of objects
return data;
}
}
I need to return my IList from the MyMethod() method based on the name of the object I'm passing in as a string.
I could just do a switch/case on the string and then instantiate the MyClass within the case with the reference to the "real" object, but I'm wondering if there's a better (shorter and cleaner) way of doing this.
TIA
Your wrapper got the following signature:
public class MyClass<T> where T : class, new()
it basically says "T needs to be a class and have a default constructor". The interesting part is about the default constructor. It means that the class must have a constructor with no arguments.
It tells .NET that you can invoke:
var obj = new T();
So the first step is to do just that:
public class MyClass<T> where T : class, new()
{
public IList<T> MyMethod(Stream stream)
{
var data = new List<T>();
//this
var obj = new T();
return data;
}
}
next you wanted to invoke a method. That's done with the help of reflection.
A simple example is:
var obj = new T();
//get type information
var type = obj.GetType();
//find a public method named "DoStuff"
var method = type.GetMethod("DoStuff");
// It got one argument which is a string.
// .. so invoke instance **obj** with a string argument
method.Invoke(obj, new object[]{"a string argument"});
Update
I missed the important part:
I need to return my IList from the MyMethod() method based on the name of the object I'm passing in as a string.
If the type is declared in the same assembly as your executing code you can just pass the full type name like Some.Namespace.ClassName" toType.GetType()`:
var type = Type.GetType("Some.Namespace.ClassName");
var obj = Activator.CreateInstance(type);
If the class is declared in another assembly you need to specify it:
var type = Type.GetType("Some.Namespace.ClassName, SomeAsseblyName");
var obj = Activator.CreateInstance(type);
The rest is pretty much the same.
If you only have the class name you can traverse the assembly to find the correct type:
var type = Assembly.GetExecutingAssembly()
.GetTypes()
.FirstOrDefault(x => x.Name == "YourName");
var obj = Activator.CreateInstance(type);
It sounds like you want to create the generic type so that you can create an instance of it.
//Assuming "typeName" is a string defining the generic parameter for the
//type you want to create.
var genericTypeArgument = Type.GetType(typeName);
var genericType = typeof (MyGenericType<>).MakeGenericType(genericTypeArgument);
var instance = Activator.CreateInstance(genericType);
This assumes that you already know what the generic type is, but not the type argument for that generic type. In other words, you're trying to determine what the <T> is.
Use Reflection. Make MyMethod static. See the code below:
public object run(string typename, Stream stream)
{
var ttype = Assembly
.GetExecutingAssembly()
.GetTypes()
.FirstOrDefault(x => x.Name == typename);
MethodInfo minfo = typeof(MyClass)
.GetMethod("MyMethod", BindingFlags.Static | BindingFlags.Public);
return minfo
.MakeGenericMethod(ttype)
.Invoke(null, new object[] { stream });
}
I can create a generic class that takes, as its template parameter, a C# type, and then within the generic class use the System.Type information corresponding to that C# type:
public class Generic<T>
{
public bool IsArray()
{
return typeof(T).IsArray();
}
public T Create()
{
return blah();
}
}
Generic<int> gi = new Generic<int>();
Debug.WriteLine("int isarray=" + gi.IsArray());
Generic<DateTime> gdt;
But now let's say what I have, is a System.Type. I can't use this to instantiate my generic class:
FieldInfo field = foo();
Generic<field.FieldType> g; // Not valid!
Is there some clever C# thing I can do, to convert a System.Type back to the original C# type? Or some other way, to create a generic that can (1) give me information about the System.Type, and (2) create objects of the associate C# type?
By the way, this is a very contrived example to explain the problem I'm trying to solve, don't worry too much about whether Generic makes sense or not!
The only thing you can do is use reflection. This because while the int of Generic<int> is known at compile-time, the field.FieldType is known only at runtime.
Reflection example:
Type type = typeof(Generic<>).MakeGenericType(field.FieldType);
// Object of type Generic<field.FieldType>
object gen = Activator.CreateInstance(type);
But even here, from a Type (field.FieldType) you obtain another Type (type)
There are normally three ways of using this:
Full reflection: you use the object of type Generic<type> only through reflection. You create it through Activator.CreateInstance and from there you begin using Type.GetMethod() and Invoke()
Type type = typeof(Generic<>).MakeGenericType(field.FieldType);
// Object of type Generic<field.FieldType>
object gen = Activator.CreateInstance(type);
MethodInfo isArray = type.GetMethod("IsArray");
bool result = (bool)isArray.Invoke(gen, null);
Interfaces/base classes: you have a non-generic base class or interface that is common between all the Generic<T>. You use your object only though that interface/base class.
public class Generic<T> : IComparable where T : new()
{
public bool IsArray()
{
return typeof(T).IsArray;
}
public T Create()
{
return new T();
}
public int CompareTo(object obj)
{
return 0;
}
}
Type type = typeof(Generic<>).MakeGenericType(field.FieldType);
IComparable cmp = (IComparable)Activator.CreateInstance(type);
int res = cmp.CompareTo(cmp);
A generic method where you put all the handling of the Generic<T>. That is the only method that is used through reflection.
public static void WorkWithT<T>() where T : new()
{
Generic<T> g = new Generic<T>();
T obj = g.Create();
Console.WriteLine(g.IsArray());
}
var method = typeof(Program).GetMethod("WorkWithT").MakeGenericMethod(field.FieldType);
// Single reflection use. Inside WorkWithT no reflection is used.
method.Invoke(null, null);
Assuming you have a class, say, MainClass. Assuming this class has a property, MainProperty, whose type is also another custom class, AlternateClass. Given as...
public class MainClass
{
...
public AlternateClass MainProperty { get; set; }
...
}
public class AlternateClass
{
...
public int someAction()
{
...
}
...
}
I'd like to know how to invoke the someAction() method for MainProperty using reflection, the alternative of which is:
MainClass instanceOfMainClass = new MainClass();
instanceOfMainClass.MainProperty.someAction();
You need to get the type and an instance of each layer. Reflection gets properties and methods from the type system but performs work on instances.
Not Test, probably has a few errors in it.
//First Get the type of the main class.
Type typeOfMainClass = instanceOfMainClass.GetType();
//Get the property information from the type using reflection.
PropertyInfo propertyOfMainClass = typeOfMainClass.GetProperty("MainProperty");
//Get the value of the property by combining the property info with the main instance.
object instanceOfProperty = propertyOfMainClass.GetValue(instanceOfMainClass);
//Rinse and repeat.
Type typeofMainProperty = intanceOfProperty.GetType();
MethodInfo methodOfMainProperty = typeofMainProperty.GetMethod("someAction");
methodOfMainProperty.Invoke(instanceOfMainProperty);
You'll need to make use of the GetMethod() and GetProperty() Reflection methods. You will call the respective method on the type, and then use the returned MethodInfo or PropertyInfo object against the original object.
For example:
MainClass theMain = new MainClass();
PropertyInfo mainProp = typeof(MainClass).GetProperty("MainProperty");
AlternateClass yourAlternate = mainProp.GetValue(mainClass);
MethodInfo someActionMethod = typeof(AlternateClass).GetMethod("someAction");
someActionMethod.Invoke(yourAlternate);
Is there any way to do something like this?
void SomeMethod(Type generic)
{
SomeGenericMethod<generic>();
}
I need to pass the type in as a "normal" parameter and not a generic type parameter.
You can do it with reflection:
public class foo
{
public void SomeMethod(Type type)
{
var methodInfo = this.GetType().GetMethod("SomeGenericMethod");
var method = methodInfo.MakeGenericMethod(new[] { type });
method.Invoke(this, null);
}
public void SomeGenericMethod<T>()
{
Debug.WriteLine(typeof(T).FullName);
}
}
class Program
{
static void Main(string[] args)
{
var foo = new foo();
foo.SomeMethod(typeof(string));
foo.SomeMethod(typeof(foo));
}
}
That said, using reflection in this way means you are losing some of the benefits of using generics in the first place, so you might want to look at other design alternatives.
Assuming that your method is defined in a class called MyClass, this should do it:
var MyObject = new MyClass();
typeof(MyClass).GetMethod("SomeGenericMethod").MakeGenericMethod(generic).Invoke(myObject, null);
Type.GetMethod() gets an object which describes a method defined in the Type it's called on.
The method is generic, so we need to call MakeGenericMethod, and pass its one generic parameter.
We then invoke the method, passing the object we want the method called on and any arguments it takes. As it takes no arguments, we just pass null.