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);
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 exposes a generic method. This method receives an instance of the generic object as parameter and modifies this instance.
Example class:
public class GenericClass<T>
{
public T GenericMethod(T obj)
{
// modify the object in some (arbitrary) way
IEnumerable<FieldInfo> fields = obj.GetType().GetRuntimeFields();
foreach (FieldInfo field in fields)
{
if (field.FieldType == typeof(string))
{
field.SetValue(obj, "This field's string value was modified");
}
}
return obj;
}
}
If I have a type (abc):
public class abc
{
public string a;
public string b;
public int c;
}
I can call this method as follows:
GenericClass<abc> myGeneric = new GenericClass<abc>();
var myObject = myGeneric.GenericMethod(new abc());
//Confirm success by printing one of the fields
Console.Writeline(((abc)myObject).a);
Now, my actual question:
How would I call this same Generic method, using a type that is only known at run-time (as opposed to type abc above). I also want to instantiate this this as I pass it in to the GenericMethod, just like I did for abc above.
E.g. (I know this is completely wrong)
Type MyType;
GenericClass<MyType> myGeneric = new GenericClass<MyType>();
var myObject = myGeneric.GenericMethod(new MyType());
Due to unknown type success cannot be confirmed by printing the field "a" which may not exist, I could print the value of all string fields, but this is beyond the scope of the question.
To answer your question:
var type = typeof(abc);
object instanceToModify = new abc();
var typeToCreate = typeof(GenericClass<>).MakeGenericType(type);
var methodToCall = typeToCreate.GetMethod("GenericMethod");
var genericClassInstance = Activator.CreateInstance(typeToCreate);
methodToCall.Invoke(genericClassInstance, new[] { instanceToModify });
DEMO
But:
If your type is only known at runtime your instance must be handled in a variable declared as object or dynamic. In that case you can change your method signature to:
public object GenericMethod(object obj)
{
// modify the object in some (arbitrary) way
IEnumerable<FieldInfo> fields = obj.GetType().GetRuntimeFields();
foreach (var field in fields)
{
if (field.FieldType == typeof(string))
{
field.SetValue(obj, "This field's string value was modified");
}
}
return obj;
}
There's no need for a generic class/method.
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.
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);
I'm creating a small validation framework, I've a custom Validation Attribute which is assignable to methods and a IsValid property in ValidationCore class. When IsValid called inside the method my ValidationCore finds a caller method and get its attribute assigned to method. My custom Validation attribute has a property name named TypeToValidate. So when I find validation attribute I look for any types in class scope of that type. I don't have any problem till now, but the problem is when I want to get the value of property which I've to validate I don't have any instance of that class to get that property value. I don't know how can I handle this situation please help me.
This is my sample :
public class TestClass
{
public static TestModel Model { get; set; }
public static ModelValidator ModelState
{
get { return new ModelValidator(); }
}
[Validate(typeof(TestModel))]
public static void DoSomething()
{
if (ModelState.IsValid)
{
// Do something else....
}
}
}
Edit : This is my IsValid property
public virtual Boolean IsValid
{
get
{
// Get IsValid caller method
var method = GetCallerMethod();
// Get method attribute
var Attrib = GetMethodAttribute(typeof(ValidateAttribute), method);
// Get model to validate inside class scope
var modelProperty = GetModelToValidateInClassScope(Attrib, method);
if (modelProperty != null)
{
ValidateModel(modelProperty);
}
....
}
}
and here is ValidateModel method :
protected virtual void ValidateModel(PropertyInfo modelProperty)
{
// Here I've model property
// But I can't get its value
var model = modelProperty.GetValue(null, null);
var properties = model.GetType().GetProperties(
BindingFlags.FlattenHierarchy |
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.DeclaredOnly);
foreach (var propertyInfo in properties)
{
// Add error to error list
GetPropertyErrors(model, propertyInfo);
}
}
Thanks in advance.
You need an instance for which to get the property value of. It looks like you need to modify the method GetModelToValidateInClassScope so that it returns the instance of the model itself, along with the PropertyInfo.
If this method is changed so it looks something like this (note the new out parameter):
PropertyInfo GetModelToValidateInClassScope(Type attributeType, MethodInfo methodInfo, out object instance)
{
// same implementation as before ...
// assign the model to use,
// which is likely accessible from somewhere in the is method
instance = theModelInstanceFromThisMethod;
// .. or if the property is static, then uncomment the next line:
// instance = null;
// same return value as before ...
}
I'm just guessing here because you didn't provide the implementation of this method. If GetModelToValidateInClassScope doesn't have access to the model instance, then you'll have to get it from somewhere else.
Once you get the model instance, it could be used like the following. Note that ValidateModel has been modified so that it accepts the model instance as the first parameter.
...
// Get model to validate inside class scope
object instance; // <--- will hold the model instance
var modelProperty = GetModelToValidateInClassScope(Attrib, method, out instance);
if (modelProperty != null)
{
ValidateModel(instance, modelProperty); // <--- make sure to pass the instance along!
}
...
protected virtual void ValidateModel(object instance, PropertyInfo modelProperty)
{
// get value of instance property
var model = modelProperty.GetValue(instance, null);
...
}
Answer to the question actually asked
I don't have any problem till now, but the problem is when I want to get the value of property which I've to validate I don't have any instance of that class to get that property value.
If it's a static property, that's fine - just use null as the first argument:
// First argument is the instance: null as it's a static property
// Second argument is indexer arguments: null as we don't have any
var value = property.GetValue(null, null);
From the documentation:
Because static properties belong to the type, not individual objects, get static properties by passing null as the object argument.
Alternative approach
If you're just trying to get the TypeToValidate property from the Validate attribute, then you should have an instance of the attribute, and you can just cast to ValidateAttribute and retrieve the property directly.
Basically, it's not clear where properties really come into what you're trying to do. Your attribute is on a method rather than a property, and your attribute doesn't say which properties to validate...