C# reflective method invocation with arbitrary number of parameters [duplicate] - c#

This question already has answers here:
Calling a function using reflection that has a "params" parameter (MethodBase)
(3 answers)
Closed 8 years ago.
Trying to invoke a method with an arbitrary number of parameters:
Type objType = obj.GetType();
MethodInfo method = objType.GetMethod("InvokedMethod");
method.Invoke(obj, new string[] { "param1", "param2" });
The method signature looks like that:
public void InvokedMethod(params string[] args) { ... }
Why I get following Exception:
System.Reflection.TargetParameterCountException (Parameter count mismatch)

The method doesn't accept two parameters, it accepts one parameter that is an array. The compiler will perform a transformation such that a method call of two strings will be transformed into a call of a single array with two values. That transformation won't be done for you when using reflection. You'll need to explicitly create an array and put the two values in it:
method.Invoke(obj, new object[] { new[]{"param1", "param2"} });
Remember that Invoke doesn't accept a single value of that one parameter either. It accepts an array of all parameters. Passing new string[] { "param1", "param2" } to Invoke is telling Invoke that you have two parameters, each of which are strings. You need to wrap your one array parameter in another array so that Invoke sees that you have one parameter that is itself an array.

Try this:
method.Invoke(obj, new object[] {new string[] {"param1", "param2"}});
.Invoke takes an array of arguments that represents the entire signature. in your case the first argument should be an array of strings. So you have to pass in an array of objects with the first element being the array of strings.

Related

Using a System.Type as a Generic Parameter for a Method [duplicate]

This question already has answers here:
Calling generic method with Type variable [duplicate]
(2 answers)
How do I use reflection to call a generic method?
(8 answers)
Closed 2 years ago.
how to create a generic method for something like this
Type myType = Type.GetType(classNameType.AssemblyQualifiedName); --gets the required type
CustomerWrapper.DeserializeJsonFromStream<myType>(stream); -- failing here
I would like to pass diiferent types at the above line but getting error saying "myType is variable but used as type". How can I resolve this.?
You can't do this out-of-the-box. Generics needs to be known at compile time so the compiler can generate the appropriate IL and generated classes.
However you can use reflection with a bit of tomfoolery (given the usual reflection performance hit). Here is an overly contrived example
Given
public class CustomerWrapper
{
public void DeserializeJsonFromStream<T>(Stream stream)
=> Console.WriteLine(typeof(T).Name);
}
Usage
var wrapper = new CustomerWrapper();
var stream = new MemoryStream();
var myType = typeof(string);
var method = typeof(CustomerWrapper).GetMethod(nameof(CustomerWrapper.DeserializeJsonFromStream));
if (method == null)
throw new MissingMethodException(nameof(CustomerWrapper),nameof(CustomerWrapper.DeserializeJsonFromStream));
var genericType = method.MakeGenericMethod(myType);
genericType.Invoke(wrapper, new object[]{stream});
Output
String
Or if you feel lucky and like everything on the one line
var method = typeof(CustomerWrapper)
.GetMethod(nameof(CustomerWrapper.DeserializeJsonFromStream))
.MakeGenericMethod(myType)
.Invoke(wrapper, new object[] {stream});
Note : it's worth pointing out there is usually a type parameter in most deserialization methods Deserialize(String, Type, JsonSerializerOptions) as such you are likely better to use them.
Additional Resources
Type.GetMethod Method
Gets a specific method of the current Type.
MethodInfo.MakeGenericMethod(Type[]) Method
Substitutes the elements of an array of types for the type parameters
of the current generic method definition, and returns a MethodInfo
object representing the resulting constructed method.
MethodBase.Invoke Method
Invokes the method or constructor reflected by this MethodInfo
instance.

Unable to pass List<char> to List<object> as a parameter? [duplicate]

This question already has answers here:
Why covariance and contravariance do not support value type
(4 answers)
Closed 7 years ago.
So I have a method in my code where one of the parameters is a IEnumerable<object>. For clarity purposes, that will be the only parameter for the example. I was originally calling it with a variable that was a List<string>, but then realized I only needed those to be chars and changed the signature of the variable to List<char>. Then I received an error in my program saying:
Cannot convert source type 'System.Collections.Generic.List<char>'
to target type 'System.Collections.Generic.IEnumerable<object>'.
In code:
// This is the example of my method
private void ConversionExample(IEnumerable<object> objs)
{
...
}
// here is another method that will call this method.
private void OtherMethod()
{
var strings = new List<string>();
// This call works fine
ConversionExample(strings);
var chars = new List<char>();
// This will blow up
ConverstionExample(chars);
}
The only reason that I could possibly think of as to why the first will work, but the second won't is because a List<char>() is convertible to a string? I don't really think that would be it, but that's the only long-shot guess that I can make about why this doesn't work.
Generic argument covariance doesn't support value types; it only works when the generic argument is a reference type.
You can either make ConversionExample generic and accept an IEnumerable<T> rather than an IEnumerable<object>, or use Cast<object> to convert the List<char> to an IEnumerable<object>.
This would be my solution:
// This is the example of my method
private void ConversionExample<T>(IEnumerable<T> objs)
{
...
}
// here is another method that will call this method.
private void OtherMethod()
{
var strings = new List<string>();
// This call works fine
ConversionExample<string>(strings);
var chars = new List<char>();
// This should work now
ConversionExample<char>(chars);
}

Optional argument followed by Params [duplicate]

This question already has answers here:
C# 4.0, optional parameters and params do not work together
(3 answers)
Closed 9 years ago.
So I see that it's possible to have a method signature where the first parameter provides a default value and the second parameter is a params collection.
What I can't see is a way to actually use the default value of the first argument.
Is it at all possible?
Example method:
void WaitAllTasks(string message = "Running Task.WaitAll", params Task[] tasks);
I initially tried omitting the message parameter when calling the method and also tried using named parameters, which doesn't work with params.
It compiles, but is it possible to use it?
I can find three ways of calling the method without specifying a value for the first parameter:
using System;
class Test
{
static void PrintValues(string title = "Default",
params int[] values)
{
Console.WriteLine("{0}: {1}", title,
string.Join(", ", values));
}
static void Main()
{
// Explicitly specify the argument name and build the array
PrintValues(values: new int[] { 10, 20 });
// Explicitly specify the argument name and provide a single value
PrintValues(values: 10);
// No arguments: default the title, empty array
PrintValues();
}
}
I haven't found a way of specifying multiple values without explicitly building the array though...

Explain syntax of delegate invocation c#

In the code from this link: http://c-sharp-programming.blogspot.com/2008/07/cross-thread-operation-not-valid.html, a delegate is used to update a text box's value from a worker thread.
I can basically see what's happening, but the syntax of this line specifically:
label1.Invoke(del, new object[] { newText });
is confusing to me. Can someone explain it please? Why do we use a new object array syntax for the delegate when there's only one parameter?
Full code:
delegate void updateLabelTextDelegate(string newText);
private void updateLabelText(string newText)
{
if (label1.InvokeRequired)
{
// this is worker thread
updateLabelTextDelegate del = new updateLabelTextDelegate(updateLabelText);
label1.Invoke(del, new object[] { newText });
}
else
{
// this is UI thread
label1.Text = newText;
}
}
TL;DR:
Control.Invoke is calling DynamicInvoke on your delegate which takes an object array of parameters to work with any delegate type.
//
The keyword delegate in C# in analagous to specifying a type of function pointer. You can use that type to pass methods of a specific signature. In your example, the signature is for a method that takes 1 arg (a string) and returns nothing (void). The method updateLabelText matches that sig. The line:
updateLabelTextDelegate del = new updateLabelTextDelegate(updateLabelText);
Is just a full-text way of saying:
updateLabelTextDelegate del = updateLabelText;
Then, you can pass your variable del, which is now a pointer to the method updateLabelText to the Control.Invoke method.
label1.Invoke(del, new object[] { newText });
Which thanks to params being using in the Control.Invoke signature, you don't even have to explicitly say it's an object[]
label1.Invoke(del, newText);
The Invoke takes an array of objects, which it'll use as the args to the delegate given. (Yes your update method takes one string arg, keep reading) With your variable del, you could call updateLabelText yourself:
del(newText);
Which would essentially be the same as:
updateLabelText(newText);
Inside Control.Invoke, they are calling your del method, but it doesn't have to know how many args it takes thanks to some helper methods on delegates. You would find something like this:
EDIT I did some deep digging for science, the invocation internally is more like:
del.DynamicInvoke(args);
Where args is an object[]. For more info on things you can do with your delegate variable (which is of type Delegate), read more here.
If you look at the method signature of Control.Invoke, you'll see it takes params Object[] args. You can either pass object[] args or a single argument.
The object array is passed to the delegate's Invoke method. In this case updateLabelTextDelegate takes a single string parameter, hence the single element in the array.
In fact the array does not need to be explicitly created, and
label1.Invoke(del, newText)
is also valid.
First, it's worth noting that this isn't calling Invoke on the delegate - it's calling Invoke on the control. Now if you look at the signature of Control.Invoke being used here, it's this:
public Object Invoke(
Delegate method,
params Object[] args
)
If the method took one specific delegate type, it could take the appropriate parameter types for that delegate. In your case your delegate only takes one parameter, but suppose we wanted to pass in an Action<string, string, int> - using the very general approach above, we can do that with:
control.Invoke(someDelegate, new object[] { "foo", "bar", 10 });
So the answer is that the object[] is there to provide generality, because the delegate type is left general too. It's a bit like MethodInfo.Invoke - without knowing at compile-time how many parameters there are, a value of type object[] is the best way of allowing various situations.

Calling (params object[]) with Expression[]

I'm trying to call String.Format from with in a Linq.Expression tree. Here's a quick example:
var format = Expression.Constant("({0}) {1}");
var company = Expression.Property(input, membernames.First());
var project = Expression.Property(input, membernames.Last());
var args = new Expression[] {format, company, project};
var invoke = Expression.Call(method,args);
The issue however is that String.Format has the signature of:
String.Format(string format, params object[] args)
and I'm trying to pass in Expression[].
Now I could go through all the trouble of creating an array, populating it with the results of my expressions, but what I really want the result to be, is something like this:
String.Format("({0}) {1}", input.foo, input.bar)
How do I go about calling a params function via Linq Expressions?
What params actually does is just to specify ParamArrayAttribute for that parameter. The C# compiler understands this, and creates the array behind the scenes.
Expressions don't understand this, so you actually have to create the array by yourself, if you want to call a method with params. This can be also seen by the fact that when you assign a lambda using params-method to an expression, the expression contains the array creation:
Expression<Func<string>> expression = () => string.Format("",1,2,3,4);
string expressionString = expression.ToString();
Here, expressionString will contain this string:
() => Format("", new [] {Convert(1), Convert(2), Convert(3), Convert(4)})
To create an expression that creates an array, use the Expression.NewArrayInit() method.
That being said, if you only want two parameters (or one or three), there is an overload of string.Format() that you can use directly from an expression.
params is just syntactic sugar. Ultimately the parameter is just an array. Therefore, the parameter type should be object[] and an expression describing such an array is what you should pass as the second argument. In other words, you should only have two arguments, not three. And the second argument should be a two-element array containing what is currently your 2nd and 3rd arguments.

Categories

Resources