I have an app that loads a given DLL, discovers its methods by reflection, and then attempts to invoke particular methods in the DLL dynamically, i.e.,
method.Invoke ( dll, parms )
where 'parms' is an object[] with a single member. This works fine if the parameter is a simple intrinsic type (long, double, etc.). E.g., I can load and call the various Math methods this way.
The problem occurs when I try to pass an instance of a class (which is defined in the DLL) as a parameter, e.g.,
namespace NS
{
public class ABC
{
public long n;
public ABC ( long i )
{
n = i;
}
}
}
So, parms[0] contains new ABC ( 4 ). In this case, I get the following exception:
Object of type 'NS.ABC' cannot be converted to type 'NS.ABC'.
Should this work? What is the error message actually trying to tell me? (The called method is expecting ABC as its first/only argument, which I do verify in the code at run time via method.GetParameters().)
(much abbreviated code)
var assembly = Assembly.LoadFile ( pathtoDLL );
foreach ( var type in assembly.GetTypes () ) . . .
dynamic dll = Activator.CreateInstance ( type );
var methods = type.GetMethods ();
To invoke, I look up the desired method by name in 'methods', then:
var parms = new object[] { new ABC ( 4 ) };
return method.Invoke ( dll, parms );
Creating the argument instance by reflection, i.e., by getting a ConstructorInfo from the DLL, solves the problem. I.e., I call dll.GetType ().GetConstructors (), find the one I need (by class name) and then call constructor.Invoke ( new object[] { arg } ) to create the object.
Related
I am working on being able to dynamically invoke an instantiation of a class dynamically at run time.
I have spent the better part of this morning searching Google for an answer but I am pretty green in this world so I am sure the answers make sense, but they do not to me.
public class MSD : IGBRule
{
public MSD(GoverningBodyRulesGet_Result GBRule, int UserID)
{}
The line error and the error are both below
object v = Activator.CreateInstance(Type.GetType("RulesEngine.Rules.MSD, RulesEngine.Rules"), UserID, GBRules);
System.MissingMethodException: 'Constructor on type 'RulesEngine.Rules.MSD' not found.'
If you want to create an object and pass arguments to the constructor, you will have to provide the arguments in the correct order, matching the order you have specified in your constructor. So in your case, you want to pass the rule before the user id:
var type = Type.GetType("RulesEngine.Rules.MSD, RulesEngine.Rules");
object v = Activator.CreateInstance(type, GBRules, UserID);
If you pass the constructor arguments directly to the CreateInstance method, you will have to be careful with common types as it is possible that you are accidentally selecting a different overload that doesn’t call the correct constructor. You can avoid that by passing an object array with the arguments you want to pass:
var type = Type.GetType("RulesEngine.Rules.MSD, RulesEngine.Rules");
object[] args = new object[] { GBRules, UserID };
object v = Activator.CreateInstance(type, args);
I am using C# as my host lang and my proxy is IronPython and I am setting methods inside of my scope pragmatically. So, my tenitive problem is my proxy method for calling the proper method via refection. the parameters are string methodName and params object[] args the code content of the method is:
initalScope.Add(obj.Name.ToLower(), new Func<object[], dynamic>( param
=> ReflectionMethodProxy( obj.Name , param ) ));
this was my initial setup. My problem with this is that when i attempt to pass one parameter in ironpython, i get the error expected Array[object], got str which is understandable, However, it should take 1 parameter since the parameter for the method calls for params.
so i changed the method call to this
initalScope.Add(obj.Name.ToLower(), new Func<object, dynamic>( param
=> ReflectionMethodProxy( obj.Name , param.GetType() != typeof (Array) ? new [] { param } : param ) ));
which works for one parameter, but obviously would not work for multiple parameters.
my goal is to be able to pass multiple parameters via Ironpython and then distribute the to the correct method in c#. Below is my code. Im probably missing something obvious, but ive been at it for a while now and really need some more eyes.
the outcome of the test code below is two errors
methodcall("test1") - provides expected Array[object] got str error
methodcall("test1",3) - provides Invoke() takes exactly 1 argument (2 given)
// dummy method for format sake
public void setScope()
{
foreach (var obj in LinkedDocument.GetType().GetMethods().Where(
method => method.GetCustomAttributes(typeof(ScriptingVoidAttribute), true).Length != 0
|| method.GetCustomAttributes(typeof(ScriptingFuncAttribute), true).Length != 0))
initalScope.Add(obj.Name.ToLower(), new Func<object[], dynamic>( param
=> ReflectionMethodProxy( obj.Name , param ) ));
}
public dynamic ReflectionMethodProxy( string methodName , params object[] args )
{
return LinkedDocument.GetType().GetMethod(methodName, args.Select(obj
=> obj.GetType()).ToArray())
.Invoke(LinkedDocument, args);
}
Thanks in advance for any help.
I have some simple C# types that are used in an embedded IronPython script:
//simple type
public class Foo
{
public int a;
public int b;
}
var engine = IronPython.Hosting.Python.CreateEngine();
dynamic scope = engine.CreateScope();
scope.foo = new Foo{ a = 1, b = 2 };
engine.Execute( "print( foo.a )", scope );
I'd like to add some functionality to Foo but I cannot modify it's code. Also I'd rather not derive from it nor use extension methods, but use delegates instead. Ideally I'd like to write something like
scope.AddMemberFunction( scope.foo, "Fun",
new Func<Foo,int>( f => return f.a + f.b ) );
and then use it in the Python script directly:
print( foo.Fun() )
I thought this was exactly what could be done using the Operations but this raises an exception saying 'Foo' object has no attribute 'Fun'
engine.Operations.SetMember( scope.foo, "Fun",
new Func<Foo, int>( f => f.a + f.b ) );
Next I tried the Python way of things (say Foo is in a namespace IronPythonApp that is in and it's assembly is added to the engine.Runtime):
import IronPythonApp
def Fun( self ):
return self.a + self.b
IronPythonApp.Foo.Fun = Fun
but this gives a similar exception: can't set attributes of built-in/extension type 'Foo'
Is there a way to modify the Python class definition that ironPython internally generates?
Update
I explored some of the options as by Jeff Hardy's answer. Here are two ways that can both be made pretty scalable (just showing the straightforward way here)
ExpandoObject!
var foo = new Foo { a = 1, b = 2 };
dynamic exp = new ExpandoObject();
exp.Fun = new Func<int>( () => foo.a + foo.b );
exp.a = foo.a; //automating these is not a big deal
exp.b = foo.b; //
//now just add exp to the scope and done.
Using SetMember after all
scope.origFoo = new Foo { a = 1, b = 2 };
//the only variable thing in this string is 'origFoo'
//so it's no big deal scaling this
engine.Execute( #"
class GenericWrapper( object ) :
def __init__( self, foo ):
self.foo = foo
def __getattr__( self, name ) :
return getattr( self.foo, name )
foo = GenericWrapper( origFoo )", scope );
//ha, now scope contains something that we can mess with after all
engine.Operations.SetMember( scope.foo, "Fun",
new Func<int>( () => scope.foo.a + scope.foo.b ) );
engine.Execute( "print( foo.Fun() )", scope );
Short answer: nope, you can't modify a .NET class. By default, they don't have the necessary dynamic hooks to add members.
Your best bet is to wrap the class and add the members you want; Python makes this easy:
class FooWrapper(object):
def __init__(self, foo):
self.foo = foo
def __getattr__(self, name):
return getattr(self.foo, name)
def mymethod(self):
...
The __getattr__ special method is only called for attributes that are not part of normal attribute lookup, so mymethod() will be looked up normally but anything else will be forwarded to the underlying object.
If you need to do it on the C# side, you can achieve the same thing by subclassing DynamicObject and overloading the Try*Member functions.
EDIT: the class/method that i'm trying to run this inside is static and therefore i'm unable to pass this into the generic.Invoke
I have a static Data Access Class that i use to automatically parse data from various sources.
i was starting to re-factor it when i ran into a problem.
Im tring to pass a Type to a Generic method via reflection,
(the method then parses the type and returns the Type with a value)
my code currently looks like
Type type1 = typeof( T );
var item = (T)Activator.CreateInstance( typeof( T ), new object[] { } );
foreach (PropertyInfo info in type1.GetProperties())
{
Type dataType = info.PropertyType;
Type dataType = info.PropertyType;
MethodInfo method = typeof( DataReader ).GetMethod( "Read" );
MethodInfo generic = method.MakeGenericMethod( dataType );
//The next line is causing and error as it expects a 'this' to be passed to it
//but i cannot as i'm inside a static class
generic.Invoke( this, info.Name, reader );
info.SetValue(item,DataReader.Read<dataType>(info.Name, reader ) , null);
}
I guess DataReader.Read is the static method, right?
Therefore, change the error line like below, since you are calling the static method. There is not object, so you just pass null into Invoke method:
var value = generic.Invoke( null, new object[] {info.Name, reader} );
The type parameter to a generic method isn't an instance of Type; you can't use your variable in this way. However, you can use reflection to create the closed-generic MethodInfo you require (that is, with the type parameter specified), which would look something like this:
// this line may need adjusting depending on whether the method you're calling is static
MethodInfo readMethod = typeof(DataReader).GetMethod("Read");
foreach (PropertyInfo info in type1.GetProperties())
{
// get a "closed" instance of the generic method using the required type
MethodInfo genericReadMethod m.MakeGenericMethod(new Type[] { info.PropertyType });
// invoke the generic method
object value = genericReadMethod.Invoke(info.Name, reader);
info.SetValue(item, value, null);
}
I'm having trouble grasping reflection in C#, so I'm going to put my specific situation down and see what you guys can come up with. I've read TONS of C# reflection questions on here, but I still just simply don't get it.
So here's my situation; I'm trying to access an array which is a non-public member of a class I have access to.
Basically it's a System.Collections.CollectionBase which has an array variable called "list", but it has this parent type of OrderCollection and the reflection of it is just confusing the hell out of me.
I have to do a lot of these so a good guide or example would really help. Please let me know if you would like more information.
I blacked out the name of the namespace not because what I'm doing is not illegal by any means, but I'm trying to be first to market on this and so I'm trying to be careful.
What are you trying to use reflection at all? CollectionBase supports indexing, but only through the explicit interface implementation of IList, so you ought to be able to write:
IList list = Acct.Orders;
response = list[0];
You may need to cast the result to a more appropriate type, but I don't see any need for reflection here.
EDIT: Original answer didn't take account of explicit interface implementation.
While this may not help you, it may help others. Here is a simplified example of reflection:
using System;
using System.Reflection;
namespace TeamActivity
{
class Program
{
static void Main(string[] args)
{
// Dynamically load assembly from the provided DLL file.
Assembly CustomerAssembly = Assembly.LoadFrom( "BasicCalculations.dll" );
// Get a Type from the Assembly
Type runtimeType = CustomerAssembly.GetType( "BasicCalcuation.BasicCalc" );
// Get all methods from the Type.
MethodInfo[] methods = runtimeType.GetMethods();
// Loop through all discovered methods.
foreach ( MethodInfo method in methods )
{
Console.WriteLine( "Method name: " + method.Name );
// Create an array of parameters from this method.
ParameterInfo[] parameters = method.GetParameters();
// Loop through every parameter.
foreach ( ParameterInfo paramInfo in parameters )
{
Console.WriteLine( "\tParamter name: " + paramInfo.Name );
Console.WriteLine( "\tParamter type: " + paramInfo.ParameterType );
}
Console.WriteLine( "\tMethod return parameter: " + method.ReturnParameter );
Console.WriteLine( "\tMethod return type: " + method.ReturnType );
Console.WriteLine("\n");
}
// Invoke the Type that we got from the DLL.
object Tobj = Activator.CreateInstance( runtimeType );
// Create an array of numbers to pass to a method from that invokation.
object[] inNums = new object[] { 2, 4 };
// Invoke the 'Add' method from that Type invokation and store the return value.
int outNum = (int)runtimeType.InvokeMember( "Add", BindingFlags.InvokeMethod, null, Tobj, inNums );
// Display the return value.
Console.WriteLine( "Output from 'Add': " + outNum );
Console.WriteLine( "\nPress any key to exit." );
Console.ReadKey();
}
}
}