I am trying to invoke a generic method. The definition of the method is as follows:
public System.Collections.Generic.IList<T> Query<T>(string query, [string altUrl = ""])
where T : new()
This is from the SalesforceSharp library on github. I am trying to make an additional service layer over this call and am struggling to invoke it. See my code below.
public List<T> Query<T>()
{
//IList<Salesforce.Account> _returnList = null;
IList<T> _returnList = null;
Type _t = typeof(T);
SqlBuilder _sb = new SqlBuilder();
_sb.Table = _t.Name.ToString();
foreach (PropertyInfo p in _t.GetProperties()) _sb.Fields.Add(p.Name.ToString());
MethodInfo method = _Client.GetType().GetMethod("Query");
method = method.MakeGenericMethod(_t);
try
{
object[] _prms = new object[1];
_prms[0] = _sb.SQL;
_returnList = (IList<T>)method.Invoke(_Client, new object[] { _prms });
//_returnList = _Client.Query<Salesforce.Account>(_sb.SQL);
}
catch { }
return (List<T>)_returnList;
}
If I run this i get a Parameter Count Mismatch exception on the method.invoke line, but i am confused because if i bring in the two uncommented lines and execute without the generic call it is working ok. I have tried many combinations of string arrays wrapped in object arrays, strings in strings, etc but can't get it to go. I thought maybe it was treating the second parameter as mandatory? but adding another object to my _prms array didnt work either.
Please help!
Thanks,
Dom
For optional parameters that you don't want to specify a value for, you have to pass Type.Missing like this:
_returnList = (IList<T>)method.Invoke(_Client, new object[] { _sb.SQL, Type.Missing });
Quoting from this reference:
Use the Missing field for invocation through reflection to obtain the default value of a parameter
Please note also that another problem is that you are currently passing an object[] instead of just the query string.
The optional parameters are just syntactic sugars, when you don't supply an optional parameter compiler calls the method with the given default value. But in case of Reflection, you need to do it manually.
object[] _prms = new object[2];
_prms[0] = _sb.SQL;
_prms[1] = "";
_returnList = (IList<T>)method.Invoke(_Client, _prms);
Related
I Have the full name of a type as string and I would like to use this string to tell type of a generic method. see this
string typeStr ="Dll.NameSpace.MyType";
var list = new List<Dll.NameSpace.MyType>();//should put typeStr here
var result = call.SomeGenericMethod<...>(obj);//should put typeStr here
This answer didn't help me. If you try visiting this other one, it does not satisfy my needs.
EDITS: The generic method I'm trying to call is an extension one. I have unsuccessfully tried the following code,
method remains null
var result = repo.Get();
Type elementType =
Type.GetType("x2o_Care.Models.ViewModels.PatientViewModel");
MethodInfo method =typeof(AutoMapper.QueryableExtensions.Extensions)
.GetMethod("ProjectTo", new[] { typeof(IQueryable)});
MethodInfo generic = method.MakeGenericMethod(elementType);
var model = generic.Invoke(result.OrderBy(e => true).Take(20), null);
Your code is exactly what you need. You just need to specify exactly what overload of ProjectTo method you need as there are a few. Make sure to specify all the parameters in GetMethod/
For example:
MethodInfo method =typeof(AutoMapper.QueryableExtensions.Extensions)
.GetMethod("ProjectTo", new[] { typeof(IQueryable),
typeof(object),
typeof(Expression<Func<TDestination, object>>[])
});
Here is a list of all the ProjectTo methods:
https://github.com/AutoMapper/AutoMapper/blob/master/src/AutoMapper/QueryableExtensions/Extensions.cs
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
I'm using the following method to instantiate an object via reflection
Activator.CreateInstance(Type type, params object[] parameters)
Where "parameters" is the list of parameters passed to the constructor at runtime.
However, I'd like this process to be more intuitive to other developers on the team and rather than passing object[] array of parameters, I would like them to pass an anonymous object, e.g.
// note, invalid code
Activator.CreateInstance(typeof(MyType), new { paramName1 = "abc", paramName2 = "xyz})
Since the framework method doesn't support it, does anyone have an example of the code that translates an anonymous object into an array? Note that the order of the parameters is important to the Activator.CreateInstance() method since that's how it does parameter matching. Obviously this is error prone, that's why I'd prefer to use an anonymous type.
Any suggestions are gladly appreciated.
Alec.
I wouldn't use Activator.CreateInstance for this. I'd use Type.GetConstructors() to get all the constructors, and then find one which has the same number of parameters as the anonymous type has properties, and with the same names. If there might be multiple such constructors with different types, you'll need to add extra logic to check that each parameter type is compatible with the relevant property type.
Something like:
public object CreateInstance(Type type, Object parameterMapping)
{
var ctors = type.GetConstructors();
var properties = parameterMapping.GetType().GetProperties();
foreach (var ctor in ctors)
{
var parameters = ctor.GetParameters();
if (parameters.Length != properties.Length)
{
continue;
}
object[] args = new object[parameters.Length];
bool success = true;
for (int i = 0; i < parameters.Length;
{
var property = parameterMapping.GetType().GetProperty(parameter.Name);
if (property == null)
{
success = false;
break;
}
// TODO: Check property type is appropriate too
args[i] = property.GetValue(parameterMapping, null);
}
if (!success)
{
continue;
}
return ctor.Invoke(args);
}
throw new ArgumentException("No suitable constructor found");
}
I'm creating a generic object from DB data:
object[] data = new object[dataReader.FieldCount];
dataReader.GetValues(data);
T t = (T)Activator.CreateInstance(typeof(T), data);
But, types with no constructor error on the third line. I want to add an if:
if (typeof(T).GetConstructor(data.TypesOf()) != null)
data.TypesOf() is actually an array - Type[] - that contains all types of the objects in data.
What is the equivalent to data.TypesOf() that really works?
Or do I need to iterate data and build it myself?
I'm assuming your object[] is containing values that are, for example, an int, a string and a float, and you are trying to resolve a constructor of the form public T(int,string,float). To get the types, you could use:
var types = Array.ConvertAll(data, x => x.GetType());
But that won't actually help you much here, since that is pretty-much what Activator.CreateInstance(typeof(T), data) has already tried to do - so if Activator failed, I don't see that it is obvious that you're going to do any better - unless the key difference is that the constructor is non-public and you are going to supply some BindingFlags.
Personally, though, I would suggest that it is easier to bind by name than by position; there are tools like dapper that will do all of that for you, allowing you to use simply:
var data = conn.Query<SomeType>(sql, args).ToList();
for example:
int userId = 12345;
var users = conn.Query<User>("select * from Users where Id = #userId",
new {userId}).SingleOrDefault();
As far as I understood you are trying to get types of elememnts of an object array. So you can do something like:
var ctorArgsTypes = data.Select(d => d.GetType()).ToArray()
var ctor = typeof(T).GetConstructor(ctorArgsType);
// check if appropriate ctor exists
if (ctor == null)
throw something
T t = (T)Activator.CreateInstance(typeof(T), data);
When you look at the Activator.CreateInstance(Type, Object[]) method, for the Object[] paramter :
An array of arguments that match in number, order, and type the
parameters of the constructor to invoke. If args is an empty array or
null, the constructor that takes no parameters (the default
constructor) is invoked.
So maybe in your case, your data object is not typed for each value (if getting from a DB or a file). You need to find a way to "type" your objects in your data array.
Just use Type.GetTypeArray(object[]).
https://msdn.microsoft.com/en-us/library/system.type.gettypearray(v=vs.110).aspx
I've run into another problem using C# 4.0 with optional parameters.
How do I invoke a function (or rather a constructor, I have the ConstructorInfo object) for which I know it doesn't require any parameters?
Here is the code I use now:
type.GetParameterlessConstructor()
.Invoke(BindingFlags.OptionalParamBinding |
BindingFlags.InvokeMethod |
BindingFlags.CreateInstance,
null,
new object[0],
CultureInfo.InvariantCulture);
(I've just tried with different BindingFlags).
GetParameterlessConstructor is a custom extension method I wrote for Type.
According to MSDN, to use the default parameter you should pass Type.Missing.
If your constructor has three optional arguments then instead of passing an empty object array you'd pass a three element object array where each element's value is Type.Missing, e.g.
type.GetParameterlessConstructor()
.Invoke(BindingFlags.OptionalParamBinding |
BindingFlags.InvokeMethod |
BindingFlags.CreateInstance,
null,
new object[] { Type.Missing, Type.Missing, Type.Missing },
CultureInfo.InvariantCulture);
Optional parameters are denoted by an ordinary attribute and are handled by the compiler.
They have no effect (other than a metadata flag) on the IL, and are not directly supported by reflection (except for the IsOptional and DefaultValue properties).
If you want to use optional parameters with reflection, you'll need to manually pass their default values.
I'll just add some code... because. The code isn't pleasent, I agree, but it is fairly straight forward. Hopefully this will help someone who stumbles accross this. It is tested, though probably not as well as you would want in a production environment:
Calling method methodName on object obj with arguments args:
public Tuple<bool, object> Evaluate(IScopeContext c, object obj, string methodName, object[] args)
{
// Get the type of the object
var t = obj.GetType();
var argListTypes = args.Select(a => a.GetType()).ToArray();
var funcs = (from m in t.GetMethods()
where m.Name == methodName
where m.ArgumentListMatches(argListTypes)
select m).ToArray();
if (funcs.Length != 1)
return new Tuple<bool, object>(false, null);
// And invoke the method and see what we can get back.
// Optional arguments means we have to fill things in.
var method = funcs[0];
object[] allArgs = args;
if (method.GetParameters().Length != args.Length)
{
var defaultArgs = method.GetParameters().Skip(args.Length)
.Select(a => a.HasDefaultValue ? a.DefaultValue : null);
allArgs = args.Concat(defaultArgs).ToArray();
}
var r = funcs[0].Invoke(obj, allArgs);
return new Tuple<bool, object>(true, r);
}
And the function ArgumentListMatches is below, which basically takes the place of the logic probably found in GetMethod:
public static bool ArgumentListMatches(this MethodInfo m, Type[] args)
{
// If there are less arguments, then it just doesn't matter.
var pInfo = m.GetParameters();
if (pInfo.Length < args.Length)
return false;
// Now, check compatibility of the first set of arguments.
var commonArgs = args.Zip(pInfo, (margs, pinfo) => Tuple.Create(margs, pinfo.ParameterType));
if (commonArgs.Where(t => !t.Item1.IsAssignableFrom(t.Item2)).Any())
return false;
// And make sure the last set of arguments are actually default!
return pInfo.Skip(args.Length).All(p => p.IsOptional);
}
Lots of LINQ, and this has not been performance tested!
Also, this will not handle generic function or method calls. That makes this significantly more ugly (as in repeated GetMethod calls).
All questions disappear as you see your code decompiled:
c#:
public MyClass([Optional, DefaultParameterValue("")]string myOptArg)
msil:
.method public hidebysig specialname rtspecialname instance void .ctor([opt]string myOptArg) cil managed
As you see, optional parameter is a real separate entity that is decorated with specific attributes and has to be respected accordingly when invoking via reflection, as described earlier.
With the opensource framework ImpromptuInterface as of version 4 you can use the DLR in C# 4.0 to invoke constructors in a very late bound way and it's totally aware of constructors with named/optional arguments, this runs 4 times faster than Activator.CreateInstance(Type type, params object[] args) and you don't have to reflect the default values.
using ImpromptuInterface;
using ImpromptuInterface.InvokeExt;
...
//if all optional and you don't want to call any
Impromptu.InvokeConstructor(type)
or
//If you want to call one parameter and need to name it
Impromptu.InvokeConstructor(type, CultureInfo.InvariantCulture.WithArgumentName("culture"))
I know this is an old thread but just want to add this.
If you're not sure how many parameters exist for the method, you can do this dynamically instead:
var method = obj.GetType().GetMethod("methodName");
int? parameters = method?.GetParameters().Length;
var data = method?.Invoke(prop, (object?[]?)(parameters.HasValue ? Enumerable.Repeat(Type.Missing, parameters.Value).ToArray() : Array.Empty<object>()));
Hope this helps.