C# how do I pass optional parameters as a collection? - c#

Assuming I have 3rd party MethodA(type1 paramName1 = null, type2 paramName2 = null,...., nonNullAbleSuchAsInt paramName10 = 123).
And I want do the following by calling the same MethodA using the provided data given by the user. User may give me data for paramName2 and paramName4, or some other permutations such as [paramName2, paramName3, paramName5, paramName7] and etc.
if(user gives me data for paramName2 and nothing else)
{
MethodA(paramName2=userData2)
}
else if(user give me data for paramName2, paramName3, and nothing else)
{
MethodA(paramName2=userData2, paramName3=userData3)
}
else if(user give me data for paramName2, paramName4, and nothing else)
{
MethodA(paramName2=userData2, paramName4=userData4)
}
else if(user give me data for paramName2, paramName3, paramName4, and nothing else)
{
MethodA(paramName2=userData2, paramName3=userData3, paramName4=userData4)
}
... repeat for all permutations.
But, that's so many duplicated code.
I want to do the following. How do I do it?
MagicStorage<MethodA_Declaration> magicArgs = new MagicStorage<MethodA_Declaration>();
if(user gives me data for paramName1)
{
magicArgs.Add(paramName1, userData1);
}
if(user gives me data for paramName2)
{
magicArgs.Add(paramName2, userData2);
}
... repeat
if(user gives me data for paramName10)
{
magicArgs.Add(paramName10, userData10);
}
MethodA(magicArgs);
And if I made a mistake like userData10 is not the same type needed by paramName10, I get editor and compiler error.
Is this possible? I don't want to make a method call for all the permutations of user input data. There would be too much code to manage.

Given
MethodA(type1 paramName1 = null, type2 paramName2 = null, int paramName10 = 132).
Usage
public class data
{
public type1 ParamName1 {get;set;}
public type2 paramName2 {get;set;}
// initalize with what ever default the method takes as optional
public int paramName10 {get;set;} = 123;
}
...
// pass in all paramaters, dont both about ifs
// we can do this, because you have already figured out the defaults
// from the signature
MethodA(data.ParamName1, data.paramName2, data.paramName10);

One way would be to use reflection to call the method with an ordered array of parameters, where the user specified values are used where possible and Type.Missing used where no value was specified:
public static object InvokeWithOrderedParameters(object instance, string methodName,
IDictionary<string, object> namedParameters)
{
// Get the method to invoke
var method = instance.GetType().GetMethod(methodName);
// Get an array of ordered parameter values based on the specified named
// parameters, with a default value of "Type.Missing" for any missing names
var orderedParams = method.GetParameters().Select(param =>
{
object value;
// Set the value from our dictionary, or if that fails use "Type.Missing"
if (!namedParameters.TryGetValue(param.Name, out value))
{
value = Type.Missing;
}
return value;
}).ToArray();
// Invoke the method with the ordered parameters and return the value
return method.Invoke(instance, orderedParams);
}
With this method, we can pass the instance of our type, the name of the method to invoke, and a Dictionary<string, object> of named parameters and their values, and it will return the result of calling that method with the parameters that we specified.
As an example, here's a method that has default values for all it's parameters:
public class ThirdParty
{
public string MethodA(string arg1 = "defaultArg1", string arg2 = "defaultArg2",
string arg3 = "defaultArg3")
{
return $"{arg1}, {arg2}, {arg3}";
}
}
And we can use our reflection method to call it with as many named parameters as we like. Below I'm just giving a value for the second parameter:
public static void Main(string[] args)
{
var namedParameters = new Dictionary<string, object>
{
{"arg2", "custom Arg 2 value"}
};
var instance = new ThirdParty();
var result = InvokeWithOrderedParameters(instance, "MethodA", namedParameters);
Console.WriteLine(result.ToString());
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
As you can see, the value we specified was passed, and the default values were used where we didn't specify anything:

You can make it somewhat more readable using the switch support for pattern matching.
And of course you can replace any switch with a Dictionary and a loop, as long as the value has place for a function delegate. Of course in this case, the Key might need to have a delegate too.
Those would make the code more readable, but can not avoid the write work.
Beyond that, there is only moving the resolution of the function calls out of compile time and into runtime. A step I am not a friend off and thus have little experience in.

You can have something like
int param1 = default(int);
double param2 = default(double);
MyClass param3 = default(MyClass);
....
int param10 = 123;
ReadUserData(out param1, out param2, out param3, ..., out param10)
Then call MethodA:
MethodA(param1, param2, param3, ..., param10)
NOTE: Starting with C# 7.0 (I think) when using out parameters there is a sugar that let you declare the variables with the method call (so you don't have to declare them one by one), for example, the call to ReadUserData can be done as:
ReadUserData(out int param1, out double param2, out MyClass param3, ..., out int param10)

Related

How to check method exists in C# with certain particular signature?

I want to check if the method for certain parameters is implemented. In this scenario, I have for example these method overloads:
public class Transformations
{
public string TransformFrom(string s) { return s;}
public string TransformFrom(int i) { return "00" + i.ToString();}
public string TransformFrom(DateTime dt) { return
DateTime.ToString("MM/dd/yyyy HH:mm:ss.fff");}
}
Suppose I have decimal like:
object obj = 1M;
If I now call TransformFrom(obj), I get an exception.
Other part of my program returns unfortunately only objects, which really are concrete types like int or datetime. And I want to transform some of these objects to string so that those can be written to the log. So this is a runtime problem. Also I want that my Transformations-class is general enough to be used in other programs too, so there can be objects of any type.
Is there fast way to find out that this method overload does not exist?
Either way you are currently going to get a compile time error, even if there was an overload that accepted a decimal. You can approach this one of 2 ways depending on your needs:
To resolve this at compile time you have to cast to the correct type.
object obj = 1;
var objAsInt = (int) obj;
var result = transformationInstance.TransformFrom(objAsInt);
If there is no proper overload you will get a compile time error and you can resolve this at design time.
To resolve this at runtime use reflection to figure out if there is an overload of the type and if there is pass the instance.
object obj = 1;
var underlyingType = obj.GetType();
var method = typeof(Transformations).GetMethod("TransformFrom", new Type[] { underlyingType });
if(method != null)
{
var result = method.Invoke(transformationInstance, new []{obj});
}
It is possible to find out whether a given overload exists using reflection:
public static bool HasOverloadForArgument(Type targetType, string methodName, object arg)
{
var methodInfo = targetType.GetMethod(name: methodName, types: new[] { arg.GetType() });
return methodInfo != null;
}
Live sample
I'm not sure what is the whole idea here as there won't be much you can do when the transformation is not available during runtime.
Maybe, you'd just be best off using dynamic to avoid all the reflection stuff and caching required otherwise, e.g.
var boxedInt = (object)1;
var boxedFloat = (object)1f;
dynamic dynamicInt = boxedInt;
dynamic dynamicFloat = boxedFloat;
var intResult = new Transformations().TransformFrom(dynamicInt); // works
var floatResult = new Transformations().TransformFrom(dynamicFloat); // throws binder exception

Call dynamic method from string

I'm trying to call a method from a dynamic without knowing its name. I have difficulties to explain this in english so there's the code:
public void CallMethod(dynamic d, string n)
{
// Here I want to call the method named n in the dynamic d
}
I want something like:d.n() but with n replaced by the string.
I want this :
Type thisType = this.GetType();
MethodInfo theMethod = thisType.GetMethod(TheCommandString);
theMethod.Invoke(this, userParameters);
but with dynamic.
If you need the context to help you: I'm make an application that's support "mods", you put DLLs in the mod folder and it loads it and execute it. It works with dynamic (I have a dictionnary like this : Dictionnary<string, dynamic> instances;). I want the application to get the methods name from the library (with instances["topkek"].GetMethods();, I've already made this method) but then call the method with the string it returns. I don't know if what I said mean something (I'm french :/ )...
I'm using VS 2013 Express with the .Net framework 4.5, if you need more information to help me ask me.
you can write your method as follows -
public void CallMethod(dynamic d, string n)
{
d.GetType().GetMethod(n).Invoke(d, null);
}
If all methods are void, this could work. Otherwise you need to change it a bit.
public void CallMethod(string className, string methodName)
{
object dynamicObject;
// Here I want to call the method named n in the dynamic d
string objectClass = "yourNamespace.yourFolder." + className;
Type objectType = Type.GetType(objectClass);
if (objectType == null)
{
// Handle here unknown dynamic objects
}
else
{
// Call here the desired method
dynamicObject = Activator.CreateInstance(objectType);
System.Reflection.MethodInfo method = objectType.GetMethod(methodName);
if (method == null)
{
// Handle here unknown method for the known dynamic object
}
else
{
object[] parameters = new object[] { }; // No parameters
method.Invoke(dynamicObject, parameters);
}
}
}
I want to add another approach as solution:
In your case, the caller (developer of the mod) knows the method to call. Thus, this might helpful:
// In the main application:
public dynamic PerformMethodCall(dynamic obj, Func<dynamic, dynamic> method)
{
return method(obj);
{
// In a mod:
mainProgram.PerformMethodCall(myDynamicObj, n => n.myDynamicMethod());
// In another mod:
mainProgram.PerformMethodCall(myDynamicObj, n => n.anotherMethod());
This is a further development of the idea of Yuval Itzchakov in his commentary. He had suggested using a delegate.

How to treat function as string?

just I want to ask is it possible to Call function as ToUpper or ToLower() as string for example f.Name.Replace(" ", "_").ToLower()".ToLower()" or "ToUpper" for example if user select convert file name to ToLower() from dropdown list.it will take the value 'ToLower' and added to in of the function and executed the function call
Well, not cleanly. You could use reflection to find the method but you'd have to know the exact type that the function will be applied to since you won't get sophisticated binding like you do at compile-time. Plus you'll be limited to one method signature (in this case, no input parameters and returns a string).
To support any string function with that signature you could use:
string val = f.Name.Replace(" ", "_").ToLower();
string strCommand = "ToUpper";
MethodInfo theMethod = typeof(string).GetMethod(strCommand,new Type[] {});
string result = (string)(theMethod.Invoke(val,null));
But a simple switch seems cleaner and safer here.
The conditional operator (?:) would probably be a better option here
var fileName = f.Name.Replace(" ", "_");
fileName = ddl.SelectedValue == "ToLower" ? fileName.ToLower() : fileName.ToUpper();
Using Reflection for something as trivial as this is pretty unnecessary.
If you know the methods you want to call before, you can register them in a dictionary (key = name of the function or something user friendly, value = function that performs the conversion):
var dict = new Dictionary<string, Func<string, string>>();
dict.Add("Lowercase", x => x.ToLower());
dict.Add("Uppercase", x => x.ToUpper());
You can bind the drop down list to the dictionary (text = Key, value = Value) and perform the conversion as follows (ddl is the name of the DropDownList):
var f = (Func<string, string>)ddl.SelectedValue;
var convStr = f(inputStr);
You can create a defined dictionary of functions, which you can access by name of the action. You will get rid of any conditional statements and of all reflection. Example is given below:
var actions = new Dictionary<string, Func<string, string>>()
{
//keys may have other, human-readable values
{"ToLower", s => s.ToLower()},
{"ToUpper", s => s.ToUpper()}
};
//you will receive this from drop down list, it's the name of the action
var userSelectedOption = "ToUpper";
//string that you want to process
var myString = "some other user input";
//selecting appropriate action and passing myString to it
var transformedString = actions[userSelectedOption](myString);
//prints "SOME OTHER USER INPUT"
Console.WriteLine(transformedString);
Using reflection, you can call a methods using a string:
String ToLower = "ToLower";
String SometString = "This Is My String";
Type stringType = typeof(String);
// Get the overload of ToLower that takes no parameters
MethodInfo method = stringType.GetMethod(ToLower, System.Type.EmptyTypes);
// Invoke the method with no parameters
String newString = (String)method.Invoke(SometString, null);
MessageBox.Show(newString);
If I have understood your question correctly, you want to call a method based on a string.
You can use reflection, which is a topic in and of itself, but to get you started you can do something like:
MethodInfo chosenMethod = typeof(string).GetMethod(methodName, Types.EmptyTypes);
chosenMethod.Invoke(stringToModify, new object[]{});
But if it is a very limited set of functions you want to expose, you should just do a switch statement over the user input:
switch(methodName)
{
case "ToLower":
stringToModify.ToLower();
break;
...
You can use a simple switch statement:
switch (value) // Assuming value is of type SomeEnum
{
case SomeEnum.ToLower:
return f.ToLower()
case SomeEnum.ToUpper:
return f.ToUpper();
default:
//Do the Default
break;
}
This is how you parse what you get from the UI:
public static T ParseEnum<T>( string value )
{
return (T) Enum.Parse( typeof( T ), value, true );
}
Perhaps you might need to avoid limitations is sort of a Runtime intepreter of C#, like eval() function in JavaScript, which is not easily nor trivial to implement, but there is a good library out there called C# Eval ( http://csharp-eval.com/Download.php ) that uses Reflection internally of course. It is heavier but more effective for this, or even for much more complex escenarios.
You might use it in this way for example:
class Person
{
public string Name;
public string Process(string expression)
{
return CsEval.Eval(this, expression);
}
}
And then might call it:
var f = new Person();
f.Name = "John";
f.Name = f.Process("Name.Replace(" ", "_").ToLower()");
Of course this is just an example of how it evaluate what is in the string, as you can see you just need to pass a "context" where the expression is going to mean something, which in this case is the current class ("this").

Dynamically cast object to a type, when type is unknown at compile time

I have a method for fetching data from database, and it is generic:
public static IQueryable<T> GetData<T>(IQueryable<T> data, some more parameters)
data is unfiltered collection of db entities, and GetData does filtering, sorting, skipping, taking upon that collection...
When I provide variable of type IQueryable (T being, for example, Document) as first parameter, as I usually do, it, of course, works:
IQueryable<Document> data = ...
GetData<Document>(data, ....);
Now, I have a need to "calculate" the first parameter dynamically. For that I use LINQ Expressions which will evaluate into IQueryable, but I dont know which T at compile time. I was thinking something like this:
Expression db = Expression.Constant(new DataModelContainer());
Expression table = Expression.Property(db, tbl); /* tbl = "Documents", this is the whole point */
Type type = table.Type.GetGenericArguments()[0];
Expression call = Expression.Call(typeof(Queryable), "AsQueryable", new Type[] { type }, table);
object o = Expression.Lambda(call, null).Compile().DynamicInvoke();
At this point o INDEED IS IQueryable (IQueryable), and as such should be able to serve as an argument to GetData. But, I have only 'object' reference to it, and naturally, cannot use it like that.
So, my question is: Is there any way to dynamically cast 'o' to 'IQueryable' when o is exactly that. I know that cast is compile time thing, but I am hoping someone has a workaround of some sort. Or maybe I am trying too much.
You can make use of the dynamic feature. Dynamic is great for situations where you already have to do reflection:
dynamic o = Expressin.Lambda(...
Get a load of this fellah:
First. We presume the class surrounding your GetData < T > method is called Foo:
public static class Foo {
public static IQueryable<T> GetData<T>(IQueryable<T> data, int bar, bool bravo) {
// ... whatever
}
Then we try to reflect upon the MethodInfo of GetData < > (and by that I mean the actual template, generic definition, not a closed particularization of it). We try to obtain that (and succeed) at the birth of the Foo class.
private static readonly MethodInfo genericDefinitionOf_getData;
static Foo() {
Type prototypeQueryable = typeof(IQueryable<int>);
// this could be any IQuerable< something >
// just had to choose one
MethodInfo getData_ForInts = typeof(Foo).GetMethod(
name: "GetData",
bindingAttr: BindingFlags.Static | BindingFlags.Public,
binder: Type.DefaultBinder,
types: new [] { prototypeQueryable, typeof(int), typeof(bool) },
modifiers: null
);
// now we have the GetData<int>(IQueryable<int> data, int bar, bool bravo)
// reffered by the reflection object getData_ForInts
MethodInfo definition = getData_ForInts.GetGenericMethodDefinition();
// now we have the generic (non-invokable) GetData<>(IQueryable<> data, int bar, bool bravo)
// reffered by the reflection object definition
Foo.genericDefinitionOf_getData = definition;
// and we store it for future use
}
Then we write a non-generic variant of the method which should call the specific generic method with regard to the actual element type being sent as a parameter:
public static IQueryable GetDataEx(IQueryable data, int bar, bool bravo) {
if (null == data)
throw new ArgumentNullException("data");
// we can't honor null data parameters
Type typeof_data = data.GetType(); // get the type (a class) of the data object
Type[] interfaces = typeof.GetInterfaces(); // list it's interfaces
var ifaceQuery = interfaces.Where(iface =>
iface.IsGenericType &&
(iface.GetGenericTypeDefinition() == typeof(IQueryable<>))
); // filter the list down to just those IQueryable<T1>, IQueryable<T2>, etc interfaces
Type foundIface = ifaceQuery.SingleOrDefault();
// hope there is at least one, and only one
if (null == foundIface) // if we find more it's obviously not the time and place to make assumptions
throw new ArgumentException("The argument is ambiguous. It either implements 0 or more (distinct) IQueryable<T> particularizations.");
Type elementType = foundIface.GetGenericArguments()[0];
// we take the typeof(T) out of the typeof(IQueryable<T>)
MethodInfo getData_particularizedFor_ElementType = Foo.genericDefinitionOf_getData.MakeGenericMethod(elementType);
// and ask the TypeSystem to make us (or find us) the specific particularization
// of the **GetData < T >** method
try {
object result = getData_particularizedFor_ElementType.Invoke(
obj: null,
parameters: new object[] { data, bar, bravo }
);
// then we invoke it (via reflection)
// and obliviously "as-cast" the result to IQueryable
// (it's surely going to be ok, even if it's null)
return result as IQueryable;
} catch (TargetInvocationException ex) {
// in case of any mis-haps we make pretend we weren't here
// doing any of this
throw ex.InnerException;
// rethink-edit: Actually by rethrowing this in this manner
// you are overwriting the ex.InnerException's original StackTrace
// so, you would have to choose what you want: in most cases it's best not to rethrow
// especially when you want to change that which is being thrown
}
}
}

MethodInfo.Invoke parameter order

I'm trying to invoke a method using reflection.
Something like this:
method.Invoke(instance, propValues.ToArray())
The problem is that there isn't a way to ensure the array of parameters is in the right order. Is there a way to specific which values goes on which parameter by name? Or do I really have to make a custom binder? If so, can anyone guide me in the right direction?
Is there a way to specific which values goes on which parameter by name?
Well, you specify them in parameter order. So if you want to map specific values to specific names, you should fetch the parameter list with method.GetParameters and map them that way. For example, if you had a Dictionary<string, object> with the parameters:
var arguments = method.GetParameters()
.Select(p => dictionary[p.Name])
.ToArray();
method.Invoke(instance, arguments);
EDIT: This answer focuses on parameter types not the parameter names. If the code is obfuscated (or having different param names) then it will be difficult to map the solution that Jon Skeet has provided.
Anyway, I had been playing with this a lot.... This is what works best for me (without knowing param names) :
public object CallMethod(string method, params object[] args)
{
object result = null;
// lines below answers your question, you must determine the types of
// your parameters so that the exact method is invoked. That is a must!
Type[] types = new Type[args.Length];
for (int i = 0; i < types.Length; i++)
{
if (args[i] != null)
types[i] = args[i].GetType();
}
MethodInfo _method = this.GetType().GetMethod(method, types);
if (_method != null)
{
try
{
_method.Invoke(this, args);
}
catch (Exception ex)
{
// instead of throwing exception, you can do some work to return your special return value
throw ex;
}
}
return result;
}
so, you can call the above function:
object o = CallMethod("MyMethodName", 10, "hello", 'a');
The above call will should be able to invoke this method with matching signature:
public int MyMethodName(int a, string b, char c) {
return 1000;
}
Please note that he above example is in the scope of 'this'

Categories

Resources