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);
Related
I'm trying to write a method that would take an object with property name as a lambda parameter, and use it on passed object, but also use it on another, new object of the same type created inside that method.
The goal is to use the same property on both objects. The property name should be passed as a parameter to the method (lambda expression).
Let me show what I've written so far (doesn't compile):
Object to be used:
public class ObjectMy
{
public string Prop1 {get; set;}
}
Method in another class to be used with above object:
public class TestClass1
{
public void DoSomethingOnProperty(Expression<Func<ObjectMy,string>> propertyName)
{
var object1 = new ObjectMy();
var propertyNameProp = propertyName.Body as MemberExpression;
propertyNameProp.Member = "Test string"; // error Member is readonly
//DoSomethingOnProperty(object1.thesameproperty...)
}
}
I want to set passed in method's name property of ObjectMy's instance to "Test string"
and then recursively call DoSomethingOnProperty on another, new instance of ObjectMy, and use the same property name as given in the first call to DoSomethingOnProperty.
I'd like to call it like
DoSomethingOnProperty(obj=>obj.Prop1);
Thanks.
Try changing your method like this:
public void DoSomethingOnProperty<T>(Expression<Func<T, dynamic>> propertyName) where T : class, new()
{
var object1 = Activator.CreateInstance(typeof(T));
var methodName = (propertyName.Body as MemberExpression).Member.Name;
var propertyInfo = typeof(T).GetProperty(methodName);
typeof(T).GetProperty(methodName).SetValue(object1, Convert.ChangeType("Test string", propertyInfo.PropertyType));
//DoSomethingOnProperty(object1.thesameproperty...)
}
you could use it like
DoSomethingOnProperty<ObjectMy>(x => x.Prop1);
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.
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);
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.
I'm attempting to set a property on an object using reflection.
The property is an ICollection - if the Collection has not been instantiated, I want to get that done. My problems is that I'm having issues getting the inner type of the ICollection
This is my class
public class Report(){
public virtual ICollection<Officer> OfficerCollection { get; set; }
}
I'm trying to access the 'Officer' class defined below through reflection
public class Officer(){
public string Name{ get; set; }
}
Code snippet
Report report = new Report()
PropertyInfo propertyInfo = report.GetType().GetProperty("OfficerCollection");
object entity = propertyInfo.GetValue(report, null);
if (entity == null)
{
//How do I go about creating a new List<Officer> here?
}
Give this a whirl:
Report report = new Report();
PropertyInfo propertyInfo = report.GetType().GetProperty("Officer");
object entity = propertyInfo.GetValue(report, null);
if (entity == null)
{
Type type = propertyInfo.PropertyType.GetGenericArguments()[0];
Type listType = typeof(List<>).MakeGenericType(type);
var instance = Activator.CreateInstance(listType);
propertyInfo.SetValue(...);
}
First of all you have to get the of Officer property:
var propertyType = propertyInfo.PropertyType;
Then you to extract generic type parameter:
var genericType = propertyType.GetGenericArguments()[0];
After that invoke create a generic list:
var listType = typeof(List<>).MakeGenericType(genericType);
Finally create a new instance of generic list:
var listInstance = Activator.CreateInstance(listType);
and... Have fun ;)
EDIT:
It's nice to play sometimes with reflection, but I recommend you to do it this way:
public class Report()
{
private ICollection<Officer> officers;
public virtual ICollection<Officer> Officer
{
get
{
if(officers == null)
officers = new List<Officer>();
return officers;
}
set { officers = value; }
}
}
Ignoring the issue that this whole design sounds terrible, I'll try to answer your question. You can find the type of the property with Type type = ...GetProperty(...).PropertyType. If the type was a concrete type - instead of an interface as it currently is - you could then use System.Activator.CreateInstance(type, null) - where null means no constructor arguments - to create an instance of this concrete type. Given that your property type is actually an interface, you don't know whether you should be creating a list, array, collection or any other type that would satisfy this type. You would then need to use SetValue to assign the instance to the property, but of course we're not able to get this far.
You should take this information to reevaluate your design to not depend on reflection, and instead use generic parameterization (look at the new() constraint) and lazy initialization of properties (if you think that makes sense - we're not mind readers.)