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.
Related
How can i turn strings like these:
"call System.Console.WriteLine"
"ldstr \"hello\""
into Intructions with Operands?
If you now how to use Mono.Cecil (or Reflection.Emit), the question is more generally about parsing text to code actions.
You have some ways to do that and I can just show you an hint and you can choose your way.
First of all you need some prerequisites (If you IL text is a valid IL code these prerequisites already exist for you).
For example, you can't guess what Console.WriteLine is. Console is an assembly, type, method? same questions on WriteLine. And even if we know that WriteLine is a method, which overload we need to choose? and what about generics? So you need set a contract that define for example that the dot is the delimiter and the first section is assembly, the second is namespace, and so on.
For example:
"mscorlib.System.Console.WriteLine(string)" will be translated to System.Console.WriteLine(string)
After you have a strict contract, you need a few steps (for the WriteLine example):
Resolve the Assembly and get is ModuleDefinition
Resolve and import the Type
Resolve and import the MethodReference describe the requested method
Emit the call
One way to do that, is to keep structure of opcodes and their required action.
For example, we know that call instruction is need to emit call to static method (in most of the cases) so you need to send the operand to ParseStaticCall, there you need to parse the string and emit the call instruction
Pseudo code:
new Dictionary<string, Tuple<OpCode, Action<string>>>
{
{
"call",
Tuple.Create<OpCode, Action<string>>
(OpCodes.Call,ParseStaticCall)
}
};
static void ParseStaticCall(Opcpde opcode,
string call,
ILProcessor processor)
{
string assembly, namespaceName, type, method;
int numOfParameters;
var moduleDefenition = AssemblyResolver.Resolve(assembly).MainModule;
var methodReference =
new ReferenceFinder(moduleDefenition).
GetMethodReference(typeof (Console),
md => md.Name == methodName &&
md.Parameters.Count == numOfParameters);
processor.Emit(opcode, methodReference);
}
AssemblyResolver is an helper class to find assembly by name and path (can be a constant path). ReferenceFinder is an helper class that find type\method in a specific module.
So you need to create method and ILProccesor for the method body, then for every string you have, you need to separate the instruction opcode form the operand, then look in dictionary for the required action and pass the opcode, the operand as string and the ILProccesor.
I am working on a c# library, so we are concerned with breaking backwards compatibility, but I was wondering is it possible to change just the name of a parameter and maintain backwards compatibility because of the ability to use named parameters? An example of what I am trying to do is below
[Obsolete("use ChangeSpecificFoo(SpecificFoo specificFoo)")]
public void ChangeSpecificFoo(SpecificFoo foo)
{
_specificFoo = foo;
}
//Compile error ... already defines a member called 'ChangeSpecificFoo' with the same parameter types
public void ChangeSpecificFoo(SpecificFoo specificFoo)
{
_specificFoo = specificFoo;
}
Just changing the parameter name runs the potential risk of breaking backwards compatibility because someone could be calling the method using named parameters like ChangeSpecificFoo(foo: someSpecificFoo) , but we can't deprecate the method by adding a new method with the correct parameter name because parameter names are not included in the method signature, so the compiler sees it as a duplicate.
Is there any way around this? The only alternatives I see are changing the method name so it is not a duplicate and then deprecating the old method, or waiting until we add or remove parameters from the parameter list and changing the parameter names then(this may never happen because the method is pretty stable), or just make the change and fix any breaks that we may have from code using this library as we find them.
My first inclination for this is simple: DON'T. The name of your parameter is irrelevant outside of the method body. You're right to consider people calling it out by name, and therefore potentially breaking it. However, just changing the name of the parameter gives no real benefit.
The only possible reason for changing the name is to redefine what the method does because the old name leads to confusion. In that case, the name of the method should also be changed in order to not introduce another form of confusion. (The fact that the method signatures are identical is the first and more important reason to not change parameter names. However, this is to potentially explain why you might want to.)
If however, you are still adamant about keeping the same method signature, but altering the name, you could do this. (Again, I'm strongly recommending you either don't change it at all, or rename the method as well to continue to eliminate confusion.)
One way around this would be to have the method with both parameters, but make the second optional. Have that last parameter use the old name, and then assign it over within the method.
I would also HIGHLY recommend logging any uses of the named parameter, to see if your concern is valid about people calling it as a named parameter.
public void ChangeSpecificFoo(SpecificFoo specificFoo = null, SpecificFoo foo = null)
{
if (foo != null && specificFoo == null)
{
// Add any other details you can, especially
// to figure out who is calling this.
Log("Someone used a name parameter!!");
}
_specificFoo = specificFoo ?? foo;
}
As Dmitry Bychenko pointed out in the comments, this will not stop anyone from calling this method like so: ChangeSpecificFoo(null, new SpecificFoo()), which will trigger a logging.
His observation introduces another reason why this is a bad idea: You're now introducing ANOTHER way for people to "incorrectly" call your method. Therefore, I'll repeat my advice from the top of my answer: DON'T do this, unless you really really really need to change that parameter name.
I think my question title is a bit confusing, so let me clarify. For my purposes, I'm applying a MarshalAsAttribute to parameters and return types.
(Note: Initially I didn't think this was working at all, but this question here lead me to post this question: System.Reflection.Emit - How to add attribute to return type definition?)
So let's say I'm dynamically creating a delegate type for a method with this signature:
[return: MarshalAs(UnmanagedType.I1)]
internal static extern Boolean SomeFunction([MarshalAs(UnmanagedType.LPStr)] string argument);
With code similar to the following, I can do this fine both for the function arguments and for the return type (note I'm omitting a bunch here for brevity):
var currentParameterAttributeBuilder = methodBuilder.DefineParameter(parameterPosition, currentParameterAttributes,
parameterName);
var attributeParams = new Type[] { typeof(UnmanagedType) };
var attributeConstructorInfo = typeof(MarshalAsAttribute).GetConstructor(attributeParams);
var customParameterAttribute = new CustomAttributeBuilder(attributeConstructorInfo, new object[] { currentArgument.MarshallingType });
currentParameterAttributeBuilder.SetCustomAttribute(customParameterAttribute);
However, when I go to check via reflection that the attributes exist, such as in the following:
// This works and retrieves the MarshalAs attribute, 'param' being a parameter retrieved from a foreach iteration over type.GetMethod("Invoke").GetParameters()
var argumentCustomAttributes = param.GetCustomAttributes(typeof(MarshalAsAttribute), false);
// This doesn't work; it doesn't 'see' the MarshalAs attribute
var returnCustomAttributes = type.GetMethod("Invoke").ReturnType.GetCustomAttributes(typeof(MarshalAsAttribute), false);
So basically, the attribute for the return type can't be viewed via reflection, but it is in fact applied, as seen in this .NET Reflector screenshot (this signature differs from above but demonstrates my point):
example image <-- I don't have enough reputation to embed images, sorry.
I'm just curious if anyone knows why this is, as I spent quite a bit of time struggling with it before I stumbled across that other post and realized it was actually being applied correctly. Hopefully I've formatted this correctly; it's my first time posting.
It appears I should have paid more attention as I was using the wrong MethodInfo property.
Instead of MethodInfo's "ReturnType", I should have been using either "ReturnParameter", or for this specific case, "ReturnTypeCustomAttributes" -- either work for this purpose. "ReturnParameter" specifically mentions custom modifiers. Oops!
var returnCustomAttributes = type.GetMethod("Invoke").ReturnParameter.GetCustomAttributes(typeof(MarshalAsAttribute), false);
In our codebase, we have a battery of custom error checking functions (like those listed here) to check arguments less verbosely. For example, to check an argument for null I use:
Throw.IfNull(theArgument, "theArgument");
The one disadvantage of this approach is that R# gives the warning "possible NullReferenceException" on future uses of the value because it's not smart enough to detect this as a null check (or at least something that would fail if theArgument were null). Is there any way to indicate that this method checks against the argument being null? For example, when I try to run a static extension like Select() on such a value, R# warns me of 'possible null assignment to entity marked with NotNull attribute', but I can't find any documentation of such an attribute nor do I see it in the reference source for Enumerable.Select().
What you're asking can definitely be solved by applying ReSharper Annotations! Those are attributes that provide additional hints for ReSharper's analysis, allowing you to add ReSharper "goodness" onto your own methods and classes. I have recently recorded a webinar with JetBrains called ReSharper Secrets where I talk about and demonstrate annotations, you're welcome to watch it!
As to your question, there are 3 annotation attributes you can apply to solve your issues (and add more cool features).
Supposing the definition for IfNull is something like:
public static class Throw
{
public static void IfNull<T>(T parameter, string parameterName) where T : class
{
if (parameter == null)
throw ArgumentNullException(string.Format("Parameter {0} is null", parameterName));
}
}
You can decorate it with 3 ReSharper attributes, ContractAnnotation, NotNull and InvokerParameterName like this:
[ContractAnnotation("parameter: null => halt")]
public static void IfNull<T>([NotNull] T parameter,
[InvokerParameterName] string parameterName)
where T : class
{
...
}
Here is what those attributes do:
The first, [ContractAnnotation], tells ReSharper that if parameter is heuristically null, then this method halts the program execution, i.e. throws exception (at runtime). This is what prevents the "possible NullReferenceException" warning. The language used for defining Contract annotations is explained here.
The second is [NotNull], tells ReSharper that parameter must not be heuristically null. This gives the "Possible null assignment to entity marked with [NotNull] attribute" warning.
The third, [InvokerParameterName] is telling ReSharper that the parameterName argument is the name of one of the parameters from the calling (invoking) method, so it will provide code completion that lists all the calling method parameters. This will give a warning in ReSharper if the name is not a parameter, for example, a local variable name.
Here's a short video of these attributes in action (applied to another set of APIs, but the idea is exactly the same): http://screencast.com/t/NhGVaUr7GO3b
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.