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?
Related
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
I want to be able to pass FieldName as a string into Method that updates a table. I have looked at Linq.Expressions but this doesn't seem to expose all the details I need about the field Name and dataType and I don't really want to go through the existing string builder solution that executes a sql command directly.
public static Main(string[] args)
{
UpdateLicenceField(2284, "laststoragefullalert", DateTime.Now);
UpdateLicenceField(2284, "numberofalerts", int(tooMany);
UpdateLicenceField(2284, "lastalertmessage", "Oops");
}
public static void UpdateLicenceField(int LicenceID, string FieldName, object value)
{
using (myContext db = new MyContext())
{
Licence licence = db.Licence.Where(x => x.ID == LicenceID &&
x.Deleted == false).FirstOrDefault();
// so db.Licence has fields .laststoragefullalert .numberofalerts .lastalertmessage
// (in real life this has hundred of settings and we very often just want to update one)
// I'm tring to get a single Method that will act dynamically like the current ADO function that creates a SQL string and executes that.
// 1. check that the FieldName type is the same type as the object passed in value.
// 2. update that FieldName with value and Saves the Licence table.
}
}
You can use "System.Linq.Dynamic.Core" library from https://github.com/StefH/System.Linq.Dynamic.Core and use:
Licence licence = db.Licence.Where(FieldName + "==#0", fieldValue).FirstOrDefault();
or use a method like this:
public Expression<Func<TEntity, bool>> GetPredicate(string methodName, string propertyName, string propertyValue)
{
var parameterExp = Expression.Parameter(typeof(TEntity), "type");
var propertyExp = Expression.Property(parameterExp, propertyName);
MethodInfo method = typeof(string).GetMethod(methodName, new[] { typeof(string) });
if (method == null)
{
var containsMethods = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(m => m.Name == methodName);
foreach (var m in containsMethods)
{
if (m.GetParameters().Count() == 2)
{
method = m;
break;
}
}
}
var someValue = Expression.Constant(propertyValue, typeof(string));
var containsMethodExp = Expression.Call(propertyExp, method, someValue);
return Expression.Lambda<Func<TEntity, bool>>(containsMethodExp, parameterExp);
}
Licence licence = db.Licence.Where(GetPredicate("Contains", fieldName, fieldValue)).FirstOrDefault();
I hope it helps you.
Thanks very much for your responses. Using a bit from both Renan and Roberto's reply I put something together that does the job. It's obviously not ideal to update in this way but this is instead of having 50 or 60 functions with similar code within an automated process. There's still error checking and validations to be added but this will reduce my codebase considerably.
Thanks again Glenn.
public static int Main(string[] args)
{
UpdateLicenceField(2284, "LastStorageFullAlert", DateTime.Now);
UpdateLicenceField(2284, "IsProcessing", true);
UpdateLicenceField(2284, "RegSource", "this is a string");
UpdateLicenceField(2284, "ProcessFilesDelay", 200);
return 1;
}
public static void UpdateLicenceField(int LicenceID, string Field, object Value)
{
using (BACCloudModel BACdb = new BACCloudModel())
{
Licence licence = BACdb.Licence.Where(x => x.ID == LicenceID && x.Deleted == false).FirstOrDefault();
if (licence != null)
{
var fieldvalue = licence.GetType().GetProperty(Field);
var ftype = fieldvalue.PropertyType;
var vtype = Value.GetType();
if (ftype == vtype)
{
object[] Values = { Value };
licence.GetType().GetProperty(Field).SetMethod.Invoke(licence, Values);
BACdb.SaveChanges();
}
}
}
}
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.
I have written a class, for copying properties from one object to another, but I caught exception:
System.Reflection.TargetException: The object does not match the target type.
I checked, that fromPropValue is of correct type, is not null, etc.
Of course, property of recepient is Binary.
public class Reflector
{
public void ReflectProperties(object from, object to)
{
Type toType = to.GetType();
Type fromType = from.GetType();
var toProperties = toType.GetProperties();
foreach (var prop in toProperties)
{
var fromProp = fromType.GetProperty(prop.Name);
if (fromProp != null)
{
var propType = prop.PropertyType;
var fromPropValue = fromProp.GetValue(from, null);
if (propType == typeof(Binary))
prop.SetValue(this, (Binary)fromPropValue, null); // <-- error
else if (propType == typeof(string))
prop.SetValue(this, (string)fromPropValue, null);
else if (propType == typeof(bool))
prop.SetValue(this, (bool)fromPropValue, null);
}
}
}
}
P.S.: object from is parent of object to, and i want just to copy values of all properties from parent to child.
I think you want prop.SetValue(to, ... instead of prop.SetValue(this, ....
Also you don't need the if statements and the casts. You can just do prop.SetValue(to, fromPropValue, null);
Using Mono.Cecil I want to rewrite the following property:
public string FirstName
{
get { return _FirstName; }
set
{
_FirstName = value;
}
}
to this:
public string FirstName
{
get { return _FirstName; }
set
{
if (System.Object.Equals(_FirstName, value))
{
return;
}
_FirstName = value;
}
}
This is just a snippet of what the rewrite will be but it is where I'm having a problem.
Using Reflector I can see the following code rewrites the property as required except the call to System.Object.Equals(). If expect the IL code to be:
call bool [mscorlib]System.Object::Equals(object, object)
but it is being written as:
call instance void RewriteSharp.Person::.ctor()
The code to write the call to System.Object.Equals is:
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Call, objectEqualsMethodReference));
The method used to init objectEqualsMethodReference is:
private static MethodReference GetSystemObjectEqualsMethodReference(
AssemblyDefinition assembly
)
{
var typeReference = assembly.MainModule.GetTypeReferences()
.Single(t => t.FullName == "System.Object");
var typeDefinition = typeReference.Resolve();
var methodDefinition = typeDefinition.Methods.Single(
m => m.Name == "Equals"
&& m.Parameters.Count == 2
&& m.Parameters[0].ParameterType.Name == "Object"
&& m.Parameters[1].ParameterType.Name == "Object"
);
return methodDefinition;
}
It seems to me setMethodWriter.Create() or GetSystemObjectEqualsMethodReference() is incorrect and no amount of debugging has solved the problem.
The property being written and code to rewrite the property have the same framework target. 3.5 and 4.0 both fail.
I'm using the master branch https://github.com/jbevain/cecil to build Mono.Cecil.
Complete Code Listing
using Mono.Cecil;
using Mono.Cecil.Cil;
using System;
using System.Linq;
namespace RewriteNotifyPropertyChanged
{
class Program
{
static void Main(string[] args)
{
var rewrite = "..\\RewriteSharp.dll";
var rewritten = "..\\RewritenSharp.dll";
var typeName = "Person";
var propertyName = "FirstName";
var assembly = AssemblyDefinition.ReadAssembly(rewrite);
var typeDefinition = assembly.MainModule.Types.Single(t => t.Name == typeName);
var propertyDefintion = typeDefinition.Properties
.Single(p => p.Name == propertyName);
var setMethodWriter = propertyDefintion.SetMethod.Body.GetILProcessor();
var backingFieldReference = GetBackingFieldReference(typeDefinition, propertyName);
var objectEqualsMethodReference = GetSystemObjectEqualsMethodReference(assembly);
var firstExistingInstruction = setMethodWriter.Body.Instructions[0];
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Ldarg_0));
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Ldfld, backingFieldReference));
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Ldarg_1));
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Call, objectEqualsMethodReference));
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Brfalse_S, firstExistingInstruction));
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Ret));
assembly.Write(rewritten, new WriterParameters { WriteSymbols = true });
Console.WriteLine("Done.");
Console.ReadKey();
}
private static MethodReference GetSystemObjectEqualsMethodReference(
AssemblyDefinition assembly
)
{
var typeReference = assembly.MainModule.GetTypeReferences()
.Single(t => t.FullName == "System.Object");
var typeDefinition = typeReference.Resolve();
var methodDefinition = typeDefinition.Methods.Single(
m => m.Name == "Equals"
&& m.Parameters.Count == 2
&& m.Parameters[0].ParameterType.Name == "Object"
&& m.Parameters[1].ParameterType.Name == "Object"
);
return methodDefinition;
}
private static FieldReference GetBackingFieldReference(
TypeDefinition typeDefinition,
string propertyName
)
{
var fieldName = "_" + propertyName;
var fieldReference = typeDefinition.Fields.Single(f => f.Name == fieldName);
return fieldReference;
}
}
}
Cecil, unlike System.Reflection, makes the distinction between a reference and a definition, and those are scoped per module. It means that you can't simply use a MethodDefinition from another module inside your own. You have to create a proper reference to it. This is a process called importing in the Cecil terminology.
Concretely, GetSystemObjectEqualsMethodReference returns a method defined in the corlib, you need to create a reference to it in your module :
Replacing:
var objectEqualsMethodReference = GetSystemObjectEqualsMethodReference(assembly);
by:
var objectEqualsMethodReference = assembly.MainModule.Import (GetSystemObjectEqualsMethodReference(assembly));
And fixing the IL should make it work.
Also, while I'm at it, the method:
private static MethodReference GetSystemObjectEqualsMethodReference(AssemblyDefinition assembly)
{
var typeReference = assembly.MainModule.GetTypeReferences()
.Single(t => t.FullName == "System.Object");
var typeDefinition = typeReference.Resolve();
var methodDefinition = typeDefinition.Methods.Single(
m => m.Name == "Equals"
&& m.Parameters.Count == 2
&& m.Parameters[0].ParameterType.Name == "Object"
&& m.Parameters[1].ParameterType.Name == "Object"
);
return methodDefinition;
}
Would be better written as:
private static MethodReference GetSystemObjectEqualsMethodReference(AssemblyDefinition assembly)
{
var #object = assembly.MainModule.TypeSystem.Object.Resolve ();
return #object.Methods.Single(
m => m.Name == "Equals"
&& m.Parameters.Count == 2
&& m.Parameters[0].ParameterType.MetadataType == MetadataType.Object
&& m.Parameters[1].ParameterType.MetadataType == MetadataType.Object);
}
And
assembly.Write(rewritten, new WriterParameters { WriteSymbols = true });
Doesn't make much sense if you don't pass new ReaderParameters { ReadSymbols = true } when reading the assembly.
You could take a look at KindOfMagic codeplex project.
It does almost the same, but little bit better - it does not call Object.Equals(), but equality operator defined on the target type.
http://kindofmagic.codeplex.com