Using Reflection to call a method of a property - c#

What I'm trying to do is call the method of a property, using Reflection. I have the original Control (a ComboBox), the PropertyInfo of the property (ComboBox.Items) and the name of the method (ComboBox.Items.Add). I've tried the code below to get, alter, set but it doesn't work because Items doesn't have a setter.
PropertyInfo p = controlType.GetProperty(propertyName); // gets the property ('Items')
MethodInfo m = p.PropertyType.GetMethod(methodName); // gets the method ('Items.Add')
object o = p.GetValue(newControl, null); // gets the current 'Items'
m.Invoke(o, new object[] { newValue }); // invokes 'Add' which works
p.SetValue(newControl, o, null); // exception: 'Items' has no setter
Does anyone have any advice?
Thanks

That was quick... I changed the Invoke line to...
m.Invoke(p.GetValue(newControl, null), new object[] { newValue });
...and it worked :P

#acron, Thanks for providing a great question and answer. I want to extend your solution for a slightly different scenario for anyone looking in the future.
Facing a similar problem in the ASP.NET world I was trying to find a common way to load either a System.Web.UI.Webcontrols.DropDownList OR a System.Web.UI.HtmlControls.HtmlSelect While both of these have an "Items" property of type "ListItemCollection", with a corresponding "Add" method, they do not share a common interface (as they SHOULD... hey Microsoft...) so that casting can be used.
The additional challenge that your solution didn't provide for is the overloading of the Add method.
Without the overloads your line:
MethodInfo m = p.PropertyType.GetMethod(methodName);
works just fine. But, when the Add method is overloaded an additional parameter is called for so that the runtime can identify which overload to invoke.
MethodInfo methInfo = propInfo.PropertyType.GetMethod("Add", new Type[] { typeof(ListItem) });

The error that you are getting indicates that the property in question is read only. There is no set method defined. You will not be able to set the value for the property without a setter.
Post back with the name of the property or more context and we may be able to give you a better answer or an alternative.

Related

Read the value of an attribute from a method with parameters

I was looking for a way to get the value of an attribute and send it to a report I have to make. The short of it is I found an answer when a method has no parameters but any methods with paramaters throws an error.
My initial question of how to Read the value of an attribute from a method was answered by this question (Read the value of an attribute of a method)
Here is the code that has been working
public static void WriteStepNamesOfMethodToReport(Type classType, string methodName)
{
MethodInfo methodInfo = classType.GetRuntimeMethod(methodName, new Type[] { });
Attribute[] attributeList = (System.Attribute[])methodInfo.GetCustomAttributes(typeof(Step), true);
GaugeMessages.WriteMessage("---------------------");
foreach (Attribute attr in attributeList)
{
Step a = (Step)attr;
GaugeMessages.WriteMessage("Executed Step - {0}", a.Names.ElementAt(0));
}
GaugeMessages.WriteMessage("---------------------");
}
This is how I set up the variables to send (and yes I could make that one line, but I define it in one place and use it in many so that is the way it needs to be)
Type classType = typeof(AClassInTheProject);
GenericHelpers.WriteStepNamesOfMethodToReport(classType, nameof(AMethodNameFrom_AClassInTheProject));
The line of Code that starts with Attribute[] attribute.... is throwing an error when I try to provide a method (methodName) that has parameters in it. When I enter the "methodName" it is always just like that (no parenthesis as it will not accept those). The error produced says:
Object reference not set to an instance of an object.
I tried removing the parameter temporarily from the specific method that was throwing an error and it saw the Step attribute that I was looking for and output it to the report.
Here is the basic layout of the class I am using (same setup as all the non-parameter methods that work).
class AClassInTheProject
{
[Step("Perform the Step For AMethodNameOne"]
AMethodNameOne() // This one works
{
// Code
}
[Step("Perform the Step For AMethodNameTwo"]
AMethodNameTwo(string parameterA) // This one doesn't work
{
// Code
}
}
Background:
This is for a Gauge UIAutomation project. I need to run some steps in the UI Automation under logical conditions (If A Perform Step ...) which Gauge does not provide support for. All steps performed need to be output to the final report (GaugeMessages.....). This is a C# project. My need is not common among people int the Gauge community so it was not deemed priority enough to include a fix in the source code (which is why I'm doing this workaround). Hopefully that's detailed enough.
At the root, this is a NullReferenceException problem.
That call to GetRuntimeMethod is saying "give me a method by this name with no parameters". It's returning null because the method you want has parameters. It works when you remove the parameter because then it matches the "no parameters" condition.
If you want specific parameter types, specify them, e.g. new Type[] { typeof(string) }.
If you want any number and type of parameters, use the GetMethod overload that doesn't take a Type[] (assuming there's only one method by that name, otherwise you'll get a different exception) or use GetMethods and find the method you want from the array it returns.

Delegate to property

I would like to get a delegate to a property´s set function. This is how I do today:
var property = typeof(IApplicationState).GetProperty(propertyName);
var action = (Action<IApplicationState, T>)Delegate.CreateDelegate(typeof(Action<IApplicationState, T>), null, property.GetSetMethod());
This works, but then I have know the name of the property.
Can I do this without using the name? Something like this:
var action = (Action<IApplicationState, T>)Delegate.CreateDelegate(typeof(Action<IApplicationState, T>), null, IApplicationState.PROPERTY.GetSetMethod());
One simple option which does introduce an extra hop (but would probably have negligible performance impact) would be to just use a lambda expression:
Action<IApplicationState, T> action = (state, value) => state.Foo = value;
There's currently no way of referring to a property at compile-time in the same way as we can refer to a type with typeof - although it's on the C# team's feature request list.
EDIT: If this is in a generic method (with a type parameter of T), it's not at all clear to me that you'll be able to use this directly, as state.Foo would presumably have to be of type T (or object perhaps). We can't really help with that aspect without more context of what you're trying to achieve.

Reflection of generic method not working

I have a generic static method which registers a interface and I need to write that using c# reflection.
Services.AddService<ITBroker>(new TBrokerService());
I tried following code but it is not working
Type[] externTBrokerService = Assembly.LoadFrom("Business.dll").GetTypes();
Type[] externService = Assembly.LoadFrom("ServiceModel.dll").GetTypes();
Type iTBroker = externITBroker[12];
MethodInfo method = externService[1].GetMethods()[2];
//Gets Add Service method
MethodInfo generic = method.MakeGenericMethod(iTBroker);
//Make method generic
generic.Invoke(null,new object[] { externTBrokerService[0]});
//invoke the service
Above code gives me very generic exception of parameters.
What is the write way to write reflection for above code?
As it was in comments:
Note that externTBrokerService[0] is a Type and not an instance of that type.
Having that I feel a need to include sense of other comments as part of my answer.
Type iTBroker = externITBroker[12];
this is wrong! And sooner or later this will fail to find your type as the order of types in this collection is undetermined and can change. You should do it like this:
Type iTBroker = externITBroker.Single(x => x.Name == "ITBroker");
This is far form foolproof so be sure that the condition gives you unique result.
or simply load that type directly by (assuming that this is the AssemblyQualifiedName of your type):
Type.GetType("Business.ITBroker, Business");
To find method on your type there is a method Type.GetMethod one of its overloads will be sufficient to find your method.
To create instance of your type that needs to be passed as argument you can use
Activator.CreateInstance(brokerServiceType);

Dynamically setting type or result from invoked generic method?

My apologies if the question is somewhat unclear; I'm not entirely certain how to phrase this.
My issue is this. I have two classes, Manager<T> and Result<T>. Within Manager<T>, I have a whole raft of retrieval functions. Ordinarily, I would call Manager<T> and set its type like so:
Manager<SpecialDataType> mgr = new Manager<SpecialDataType>;
After which I set up my Result type, and fill it with my function from Manager, where 1 is a parameter for the GetItem function shown. I can then access things in my item:
Result<SpecialDataType> item = new Result<SpecialDataType>;
item = mgr.GetItem(1);
string x = item.Teaser;
OK. So now, what I'm trying to do is set the <SpecificDataType> to be filled in at run time. I think I've got half of the solution already, using generic types, like so:
Type generalType= Type.GetType("SpecificDataType");
Type managerType= typeof(Manager<>).MakeGenericType(generalType);
var managerInstance= Activator.CreateInstance(managerType);
object[] args = {1};
MethodInfo getItemMethod = managerInstance.GetMethod("GetItem");
But here's where I get stuck. There are specific properties that my Result class has that I need to be able to access. They are, or course, set by the data type I'm casting into. When I do an Invoke, like so:
var item = getItemMethod.Invoke(managerInstance, args);
I'm not getting any of my properties that I know are part of item. That makes sense, I suppose, because we don't know what item is. So, we tried this:
Type dataType = typeof(SmartFormData<>).MakeGenericType(sfType);
var item = Activator.CreateInstance(dataType);
item = getItemMethod.Invoke(managerInstance, args);
And got the same result. I can't seem to get to item.Teaser.
I'm not a c# coder natively (as though that's not apparent already from this overly complicated question I'm asking), so I'm not incredibly familiar with reflection and generic types. Can anyone point me in the right direction for how to solve this problem, or how to approach it from a different angle? The only caveat is that I cannot modify the Manager<T> and Result<T> functions; I have to use what I'm given there.
Thanks in advance for any help you can offer.
As Dark Falcon correctly notes in his comment you will have to use reflection to get the members of your item.
Or, if you are in .NET 4 or above, you can use the dynamic keyword to greatly simplify things:
Type generalType= Type.GetType("SpecificDataType");
Type managerType= typeof(Manager<>).MakeGenericType(generalType);
dynamic managerInstance = Activator.CreateInstance(managerType);
var item = managerInstance.GetItem(1);
string x = item.Teaser;
You need to cast the invocation result to the type you're expecting
var item = (Result<SpecificDataType>)getItemMethod.Invoke(managerInstance, args);

.NET 2.0: Invoking Methods Using Reflection And Generics Causes Exception

I'm new to Stack Overflow, so forgive me. I've just started transititoning over to C# and I am stuck on a problem.
I am wanting to pass a generic class in and call a method from that class. So, my code looks as such:
public void UpdateRecords<T>(T sender) where T : new() //where T: new() came from Resharper
{
Type foo = Type.GetType(sender.GetType().ToString());
object[] userParameters = new object[2];
userParameters[0] = x;
userParameters[1] = y;
sender = new T(); //This was to see if I could solve my exception problem
MethodInfo populateRecord = foo.GetMethod("MethodInOtherClass");
populateMethod.Invoke(sender, userParameters);
}
Exception thrown: "Object reference not set to an instance of an object."
Again, I really apologize, as I am nearly brand new to C# and this is the first time I've ever handled reflection and generics. Thank you!
First of all, I would recommend running this code in the debugger and turning one "Break on Exception" to help isolate which line causes the error. This is a useful debugging technique that can help you find these types of problems more quickly in the future. Go to Debug >> Exceptions in VS and check the checkbox for Common Language Runtime Exceptions in the Thrown column.
Now for your issue. It's likely that sender is passed in as null. If so, the line:
Type foo = Type.GetType(sender.GetType().ToString());
will throw a NullReferenceException. Instead, you can use:
Type foo = typeof(T);
which identifies the type of the generic paramter without requiring an instance of it.
Now, without knowing more about what your code is trying to do, it's impossible to say whether instantiating an instance of T is the right thing to do. Just because ReSharper recommends adding where T : new() doesn't mean it's appropriate - unless you know that that's the right behavior.
Finally, I don't know if there is a compelling reason for using reflection to invoke MethodInOtherClass - perhaps there is. But since you're new to C#, I'll mention that if the type T will always be a subclass of some base type A or will always implement some interface I that includes the method you want to call, you can simply apply a generic constraint to let the compiler know this. Then you can call the method without reverting to using reflection:
public void UpdateRecords<T>(T sender)
where T : SomeBaseClass_Or_SomeInterface_ThatDefinesMethod
{
sender = new T();
sender.MethodInOtherClass( x, y );
}
Much nicer.
One final comment. It's unusual to pass in an argument to a method, and then ignore it completely - only to instantiate an instance within the method. There are cases when it's appropriate - but I tend to view it as a code smell. If possible, I would try to either get rid of the sender argument, or change the code to first test it for null and instantiate only then.
sender.GetType().ToString() returns the type name without the assembly name.
Type.GetType expects a type name with an assembly name (unless the type is in the executing assembly or mscorlib). If the type cannot be found (e.g., due to the missing assembly name), it returns a null reference.
Try changing your code to
Type foo = sender.GetType();
or even just
Type foo = typeof(T);
You should just be able to do this to get the type:
Type foo = typeof(T);
You didn't specify where you're getting the NullReferenceException, but I'm wondering if foo is coming back as null...
Either:
sender is null, so sender.GetType() will fail.
foo.GetMethod("MethodInOtherClass") returns null, so populateMethod.Invoke() will fail.
MethodInOtherClass depends on certain preconditions (non-null references), so it will fail when those are absent.

Categories