I am trying to invoke a method via reflection with parameters and I get:
object does not match target type
If I invoke a method without parameters, it works fine. Based on the following code if I call the method Test("TestNoParameters"), it works fine. However if I call Test("Run"), I get an exception. Is something wrong with my code?
My initial purpose was to pass an array of objects e.g. public void Run(object[] options) but this did not work and I tried something simpler e.g. string without success.
// Assembly1.dll
namespace TestAssembly
{
public class Main
{
public void Run(string parameters)
{
// Do something...
}
public void TestNoParameters()
{
// Do something...
}
}
}
// Executing Assembly.exe
public class TestReflection
{
public void Test(string methodName)
{
Assembly assembly = Assembly.LoadFile("...Assembly1.dll");
Type type = assembly.GetType("TestAssembly.Main");
if (type != null)
{
MethodInfo methodInfo = type.GetMethod(methodName);
if (methodInfo != null)
{
object result = null;
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
if (parameters.Length == 0)
{
// This works fine
result = methodInfo.Invoke(classInstance, null);
}
else
{
object[] parametersArray = new object[] { "Hello" };
// The invoke does NOT work;
// it throws "Object does not match target type"
result = methodInfo.Invoke(methodInfo, parametersArray);
}
}
}
}
}
Change "methodInfo" to "classInstance", just like in the call with the null parameter array.
result = methodInfo.Invoke(classInstance, parametersArray);
You have a bug right there
result = methodInfo.Invoke(methodInfo, parametersArray);
it should be
result = methodInfo.Invoke(classInstance, parametersArray);
A fundamental mistake is here:
result = methodInfo.Invoke(methodInfo, parametersArray);
You are invoking the method on an instance of MethodInfo. You need to pass in an instance of the type of object that you want to invoke on.
result = methodInfo.Invoke(classInstance, parametersArray);
The provided solution does not work for instances of types loaded from a remote assembly. To do that, here is a solution that works in all situations, which involves an explicit type re-mapping of the type returned through the CreateInstance call.
This is how I need to create my classInstance, as it was located in a remote assembly.
// sample of my CreateInstance call with an explicit assembly reference
object classInstance = Activator.CreateInstance(assemblyName, type.FullName);
However, even with the answer provided above, you'd still get the same error. Here is how to go about:
// first, create a handle instead of the actual object
ObjectHandle classInstanceHandle = Activator.CreateInstance(assemblyName, type.FullName);
// unwrap the real slim-shady
object classInstance = classInstanceHandle.Unwrap();
// re-map the type to that of the object we retrieved
type = classInstace.GetType();
Then do as the other users mentioned here.
I tried to work with all the suggested answers above but nothing seems to work for me. So i am trying to explain what worked for me here.
I believe if you are calling some method like the Main below or even with a single parameter as in your question, you just have to change the type of parameter from string to object for this to work. I have a class like below
//Assembly.dll
namespace TestAssembly{
public class Main{
public void Hello()
{
var name = Console.ReadLine();
Console.WriteLine("Hello() called");
Console.WriteLine("Hello" + name + " at " + DateTime.Now);
}
public void Run(string parameters)
{
Console.WriteLine("Run() called");
Console.Write("You typed:" + parameters);
}
public static string StaticString()
{
return "static string example";
}
public string TestNoParameters()
{
Console.WriteLine("TestNoParameters() called");
return ("TestNoParameters() called");
}
public void Execute(object[] parameters)
{
Console.WriteLine("Execute() called");
Console.WriteLine("Number of parameters received: " + parameters.Length);
for(int i=0;i<parameters.Length;i++){
Console.WriteLine(parameters[i]);
}
}
}
}
Then you have to pass the parameterArray inside an object array like below while invoking it. The following method is what you need to work
private object ExecuteWithReflection(string methodName,object parameterObject = null)
{
Assembly assembly = Assembly.LoadFile("Assembly.dll");
Type typeInstance = assembly.GetType("TestAssembly.Main");
MethodInfo methodInfo = typeInstance.GetMethod(methodName);
ParameterInfo[] parameterInfo = methodInfo.GetParameters();
object result = null;
if (typeInstance != null) //non static
{
if(methodInfo.IsStatic == false)
{
//instance is needed to invoke the method
object classInstance = Activator.CreateInstance(typeInstance, null);
if (parameterInfo.Length == 0)
{
// there is no parameter we can call with 'null'
result = methodInfo.Invoke(classInstance, null);
}
else
{
result = methodInfo.Invoke(classInstance,new object[] { parameterObject } );
}
}
else //handle static
{
if (parameterInfo.Length == 0)
{
// there is no parameter we can call with 'null'
result = methodInfo.Invoke(null, null);
}
else
{
result = methodInfo.Invoke(null,new object[] { parameterObject } );
}
}
}
return result;
}
This method makes it easy to invoke the method, it can be called as following
ExecuteWithReflection("Hello");
ExecuteWithReflection("Run","Vinod");
ExecuteWithReflection("TestNoParameters");
ExecuteWithReflection("Execute",new object[]{"Vinod","Srivastav"});
ExecuteWithReflection("StaticString");
I'am posting this answer because many visitors enter here from google for this problem.
string result = this.GetType().GetMethod("Print").Invoke(this, new object[]{"firstParam", 157, "third_Parammmm" } );
when external .dll -instead of this.GetType(), you might use typeof(YourClass).
I would use it like this, its way shorter and it won't give any problems
dynamic result = null;
if (methodInfo != null)
{
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
result = methodInfo.Invoke(classInstance, parameters.Length == 0 ? null : parametersArray);
}
Assembly assembly = Assembly.LoadFile(#"....bin\Debug\TestCases.dll");
//get all types
var testTypes = from t in assembly.GetTypes()
let attributes = t.GetCustomAttributes(typeof(NUnit.Framework.TestFixtureAttribute), true)
where attributes != null && attributes.Length > 0
orderby t.Name
select t;
foreach (var type in testTypes)
{
//get test method in types.
var testMethods = from m in type.GetMethods()
let attributes = m.GetCustomAttributes(typeof(NUnit.Framework.TestAttribute), true)
where attributes != null && attributes.Length > 0
orderby m.Name
select m;
foreach (var method in testMethods)
{
MethodInfo methodInfo = type.GetMethod(method.Name);
if (methodInfo != null)
{
object result = null;
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
if (parameters.Length == 0)
{
// This works fine
result = methodInfo.Invoke(classInstance, null);
}
else
{
object[] parametersArray = new object[] { "Hello" };
// The invoke does NOT work;
// it throws "Object does not match target type"
result = methodInfo.Invoke(classInstance, parametersArray);
}
}
}
}
I m invoking the weighted average through reflection. And had used method with more than one parameter.
Class cls = Class.forName(propFile.getProperty(formulaTyp));// reading class name from file
Object weightedobj = cls.newInstance(); // invoke empty constructor
Class<?>[] paramTypes = { String.class, BigDecimal[].class, BigDecimal[].class }; // 3 parameter having first is method name and other two are values and their weight
Method printDogMethod = weightedobj.getClass().getMethod("applyFormula", paramTypes); // created the object
return BigDecimal.valueOf((Double) printDogMethod.invoke(weightedobj, formulaTyp, decimalnumber, weight)); calling the method
On .Net 4.7.2 to invoke a method inside a class loaded from an external assembly you can use the following code in VB.net
Dim assembly As Reflection.Assembly = Nothing
Try
assembly = Reflection.Assembly.LoadFile(basePath & AssemblyFileName)
Dim typeIni = assembly.[GetType](AssemblyNameSpace & "." & "nameOfClass")
Dim iniClass = Activator.CreateInstance(typeIni, True)
Dim methodInfo = typeIni.GetMethod("nameOfMethod")
'replace nothing by a parameter array if you need to pass var. paramenters
Dim parametersArray As Object() = New Object() {...}
'without parameters is like this
Dim result = methodInfo.Invoke(iniClass, Nothing)
Catch ex As Exception
MsgBox("Error initializing main layout:" & ex.Message)
Application.Exit()
Exit Sub
End Try
Related
I'm trying to call the equivalent of this code via reflection:
prop.WhenAnyValue(s => s.Value).Skip(1).Subscribe(...);
This is the reflection code I use to "solve" the WhenAnyValue part:
MethodInfo[] whenAnyMethods = typeof(WhenAnyMixin).GetMethods();
MethodInfo whenAnyMethod = new Func<MethodInfo>(() =>
{
foreach (var mi in whenAnyMethods)
{
if (mi.Name == "WhenAnyValue" && mi.GetGenericArguments().Length == 2)
{
return mi;
}
}
return null;
})();
MethodInfo whenAnyMethodGeneric = whenAnyMethod.MakeGenericMethod(new Type[] { containingType, propertyType });
var whenAnyResult = whenAnyMethodGeneric.Invoke(null, new object[] { containingObject, expression });
The containing object derives from ReactiveObject (ReactiveUI).
The type of the result is exactly the same as when I'm calling WhenAnyValue directly on a property I'm interested in (without reflection), so I think I got this part right. :)
Now I'm trying to skip:
MethodInfo[] methods = typeof(Observable).GetMethods();
MethodInfo skipMethod = new Func<MethodInfo>(() =>
{
foreach (var mi in methods)
{
if (mi.Name == "Skip" && mi.GetParameters().Length == 2 && mi.GetParameters()[1].ParameterType == typeof(int))
{
return mi;
}
}
return null;
})();
MethodInfo skipMethodGeneric = skipMethod.MakeGenericMethod(new Type[] { whenAnyResult.GetType() });
var skipResult = skipMethodGeneric.Invoke(null, new object[] { whenAnyResult, 1 }); <<< this line throws the exception
On execution of the last line an exception is thrown:
System.ArgumentException: "Object of type 'System.Reactive.Linq.ObservableImpl.Select`2+Selector[ReactiveUI.IObservedChange`2[type1,type2],type3]' cannot be converted to type 'System.IObservable`1[System.Reactive.Linq.ObservableImpl.Select`2+Selector[ReactiveUI.IObservedChange`2[type1,type2],type3]]'."
And here I'm stuck. What am I missing?
I'm writing a 'quick' c# application that will allow me to see all the Methods avaliable under an Assembly DLL, so far all is well and i can output the class, access modifier and name :
private void btnListMethodName()
{
string sAssemblyFileName = assemblyLocation.Text;
if (sAssemblyFileName.Length != 0)
{
Assembly assem = Assembly.LoadFrom(sAssemblyFileName);
Type[] types = assem.GetTypes();
ArrayList arrl = new ArrayList();
foreach (Type cls in types)
{
try
{
//Add Class Name
arrl.Add(cls.FullName);
if (cls.IsAbstract)
arrl.Add("Abstract Class:" + cls.Name.ToString());
else if (cls.IsPublic)
arrl.Add("Public Class:" + cls.Name.ToString());
else if (cls.IsSealed)
arrl.Add("Sealed Class:" + cls.Name.ToString());
MemberInfo[] methodName = cls.GetMethods();
foreach (MemberInfo method in methodName)
{
method.ReflectedType.GetProperties();
if (method.ReflectedType.IsPublic)
arrl.Add("\tPublic - " + method.Name.ToString());
else
arrl.Add("\tNon-Public - " + method.Name.ToString());
}
}
catch (System.NullReferenceException)
{
Console.WriteLine("Error msg");
}
}
olvMain.SetObjects(arrl);
for (int i = 0; i < arrl.Count; i++)
{
AssemblyList.Items.Add(arrl[i].ToString());
}
}
}
What i now need to output is the datatype and name of the methods signature values. For example, at the moment i output 'Public - methodName' what i would like to output is 'Public - methodName(string methodData, int methodData2)'
Is this possible?
You can obtain a method's parameters like this;
ParameterInfo[] pars = method.GetParameters();
To do that you need to change;
MemberInfo[] methodName = cls.GetMethods();
To
MethodInfo[] methodName = cls.GetMethods();
Refer MethodBase.GetParameters
Well, you can use
method.GetParameters()
Wich will return an array of ParameterInfo.
Then, ParameterInfo has a Name and a ParameterType (which has a Name) property.
I am working on "plugin" system to my program. I want to redistribute multiple dll's for example: "myAwesomePlugin1.dll", "myAwesomePlugin2.dll". Each of these files have method called for example: "doSmthAwesome(string msg)".
Now, when I have all prepared I want to load these two methods and run them:
1st doSmthAwesome from Plugin1
2nd doSmthAwesome from Plugin2
Unfortunatelly, I don't know why, but when I load these dll's, second one copies method from first dll. How I can do it in proper way, to have both these methods?
Heres my code:
string doSmthAwesomeFrom1(string msg)
{
string value = "";
try
{
Assembly a = null;
a = Assembly.LoadFrom(pluginDirectory + "/" + IkarosConfiguration.getValueFromKey("FIRST_PLUGIN"));
Type classType = a.GetType("awesomePlugin.awesomePlugin");
MethodInfo mi = classType.GetMethod("doSmthAwesome");
object obj = Activator.CreateInstance(classType);
value = mi.Invoke(obj, new object[] { msg});
}
catch
{
}
return value;
}
string doSmthAwesomeFrom2(string msg)
{
string value = "";
try
{
Assembly a = null;
a = Assembly.LoadFrom(pluginDirectory + "/" + IkarosConfiguration.getValueFromKey("SECOND_PLUGIN"));
Type classType = a.GetType("awesomePlugin.awesomePlugin");
MethodInfo mi = classType.GetMethod("doSmthAwesome");
object obj = Activator.CreateInstance(classType);
value = mi.Invoke(obj, new object[] { msg});
}
catch
{
}
return value;
}
I have a fairly simple method:
public static LinkItemCollection ToList<T>(this LinkItemCollection linkItemCollection)
{
var liCollection = linkItemCollection.ToList(true);
var newCollection = new LinkItemCollection();
foreach (var linkItem in liCollection)
{
var contentReference = linkItem.ToContentReference();
if (contentReference == null || contentReference == ContentReference.EmptyReference)
continue;
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
IContentData content = null;
var epiObject = contentLoader.TryGet(contentReference, out content);
if (content is T)
newCollection.Add(linkItem);
}
return newCollection;
}
This works fine - I can call the method and provide a type as T. However, what I want to be able to do is to be able to specify multiple types. I therefore, wrongly, assumed I could refactor the method to:
public static LinkItemCollection ToList(this LinkItemCollection linkItemCollection, Type[] types)
{
var liCollection = linkItemCollection.ToList(true);
var newCollection = new LinkItemCollection();
foreach (var linkItem in liCollection)
{
var contentReference = linkItem.ToContentReference();
if (contentReference == null || contentReference == ContentReference.EmptyReference)
continue;
foreach (var type in types)
{
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
IContentData content = null;
var epiObject = contentLoader.TryGet(contentReference, out content);
if (content is type)
newCollection.Add(linkItem);
}
}
return newCollection;
}
However, Visual Studio is showing it cannot resolve the symbol for type on the line if(content is type).
I know I'm doing something wrong, and I'm guessing I need to use Reflection here.
What you're looking for is:
type.IsAssignableFrom(content.GetType())
is is only used for checking against a type known at compile time, not at runtime.
Question:
I wrote a method to retrieve a SQL result as a list of a class instead of a datatable.
The problem is, I have a int field in the database, which is nullable.
If I hit a row with a NULL int, DataReader returns DbNull.Value instead of null.
So System.Convert.ChangeType(objVal, fi.FieldType) throws an exception, because it can't convert DbNull to an int.
So far so bad.
I thought I had solved the problem, when I just compared objVal to DbNull.Value and if true, did this instead:
System.Convert.ChangeType(null, fi.FieldType)
unfortunately, I just realized, the resulting integer type is 0 instead of NULL.
So I just tried changing the int type in my class to Nullable<int>, but now I have the problem that when a value is not DbNull.Value, ChangeType throws an exception because it can't convert int to nullable<int>...
So now I try to detect the type of the object returned by datareader, and convert it to a nullable value.
tTypeForNullable is correctly shown as Nullable<int>.
But when I look at the result type, I get: int.
Why is that ? And more important: How can I do that properly ?
Please note that because type is an object, I can't use a generic method to create Nullable<int>.
bool bisnull = IsNullable(objVal);
bool bisnullt = IsNullable(fi.FieldType);
if (bisnullt)
{
Type tTypeForNullable = typeof(Nullable<>).MakeGenericType(objVal.GetType());
//object result = Activator.CreateInstance(tTypeForNullable, new object[] { objVal });
//object result = Activator.CreateInstance(typeof(Nullable<int>), new object[] { objVal });
object result = Activator.CreateInstance(tTypeForNullable, objVal);
Type tres = result.GetType();
fi.SetValue(tThisValue, System.Convert.ChangeType(result, fi.FieldType));
}
Here's the complete routine for reference:
public virtual System.Collections.Generic.IList<T> GetList<T>(System.Data.IDbCommand cmd)
{
System.Collections.Generic.List<T> lsReturnValue = new System.Collections.Generic.List<T>();
T tThisValue = default(T);
Type t = typeof(T);
lock (cmd)
{
using (System.Data.IDataReader idr = ExecuteReader(cmd))
{
lock (idr)
{
while (idr.Read())
{
//idr.GetOrdinal("")
tThisValue = Activator.CreateInstance<T>();
// Console.WriteLine(idr.FieldCount);
for (int i = 0; i < idr.FieldCount; ++i)
{
string strName = idr.GetName(i);
object objVal = idr.GetValue(i);
System.Reflection.FieldInfo fi = t.GetField(strName);
//Type tttttt = fi.FieldType;
if (fi != null)
{
//fi.SetValue(tThisValue, System.Convert.ChangeType(objVal, fi.FieldType));
if (objVal == System.DBNull.Value)
{
objVal = null;
fi.SetValue(tThisValue, null);
}
else
{
//System.ComponentModel.TypeConverter conv = System.ComponentModel.TypeDescriptor.GetConverter(fi.FieldType);
bool bisnull = IsNullable(objVal);
bool bisnullt = IsNullable(fi.FieldType);
if (bisnullt)
{
Type tTypeForNullable = typeof(Nullable<>).MakeGenericType(objVal.GetType());
//object result = Activator.CreateInstance(tTypeForNullable, new object[] { objVal });
//object result = Activator.CreateInstance(typeof(Nullable<int>), new object[] { objVal });
object result = Activator.CreateInstance(tTypeForNullable, objVal);
Type tres = result.GetType();
fi.SetValue(tThisValue, System.Convert.ChangeType(result, fi.FieldType));
}
fi.SetValue(tThisValue, System.Convert.ChangeType(objVal, fi.FieldType));
}
}
else
{
System.Reflection.PropertyInfo pi = t.GetProperty(strName);
if (pi != null)
{
//pi.SetValue(tThisValue, System.Convert.ChangeType(objVal, pi.PropertyType), null);
if (objVal == System.DBNull.Value)
{
objVal = null;
pi.SetValue(tThisValue, null, null);
}
else
{
pi.SetValue(tThisValue, System.Convert.ChangeType(objVal, pi.PropertyType), null);
}
}
// Else silently ignore value
} // End else of if (fi != null)
//Console.WriteLine(strName);
} // Next i
lsReturnValue.Add(tThisValue);
} // Whend
idr.Close();
} // End Lock idr
} // End Using idr
} // End lock cmd
return lsReturnValue;
} // End Function GetList
with this:
public System.Data.IDataReader ExecuteReader(System.Data.IDbCommand cmd)
{
System.Data.IDataReader idr = null;
lock(cmd)
{
System.Data.IDbConnection idbc = GetConnection();
cmd.Connection = idbc;
if (cmd.Connection.State != System.Data.ConnectionState.Open)
cmd.Connection.Open();
idr = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
} // End Lock cmd
return idr;
} // End Function ExecuteReader
Please note that because type is an object, I can't use a generic method to create Nullable<int>.
You're boxing - and the result of a boxing operation for a nullable value type is never a boxed value of that type. It's either null or a non-nullable value type. See MSDN for more information.