C# Reflection How-to? - c#

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();
}
}
}

Related

Dynamic method invoke with an instance of a class as an argument

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.

How to identify generic objects in exception logs?

I have a helper method that accepts parameters with generic types. This method can throw exceptions, which I log in order to debug later. Some objects have ids, some have names, etc., that would be useful to know when trying to debug. How can I log this information in a useful way?
Regardles if it is a generic method or not, unless your object is null, you can log the type with myObject.GetType().ToString()
Loger.log("Object type that has thrown an error is" + myObj.GetType().ToString());
EDIT:
If you would like to read more information from the object then you can read object properties like this:
Type type = myObj.GetType();
List<string> info = new List<String>();
info.Add("Object type that has thrown an error is: " + type.ToString() + Environment.NewLine);
foreach (var prop in type.GetProperties())
{
if (prop.CanRead)
{
info.Add("Name: " + prop.Name + " , Value: " + prop.GetValue(myObj) + Environment.NewLine);
}
}
Logger.log(string.Join("", info));
This should work without any problems for primitives. For Objects it depends on how the ToString method is implemented. Also note that it will not read recursively, just the direct members of the class.
This function returns a C# style type name (something like List<String> or List<Dictionary<Int32,String>>) from a type.
public static string FancyTypeName(Type type)
{
var typeName = type.Name.Split('`')[0];
if (type.IsGenericType)
{
typeName += string.Format("<{0}>", string.Join(",",
type.GetGenericArguments().Select(v => FancyTypeName(v)).ToArray())
);
}
return typeName;
}

How Do I Create an Expression<Func<>> with Type Parameters from a Type Variable

I'd like to write a statement like the following:
Expression<Func<AClass, bool>> filter = x => true;
Except instead of AClass, I'd like to use a Type variable determined at runtime. So, something conceptually like this:
Type aClassType = methodParameter.GetType();
Expression<Func<aClassType, bool>> filter = x => true;
Obviously the syntax will be quite a bit different. I'm assuming I'll need to use some sort of reflection or other fanciness.
End Goal
The end goal here is a bit complex, so I've simplified things immensely for the above example. The actual .Where call that is going to use this delegate looks more like this:
var copy = iQ;
...
copy = copy.Where( p1 => iQ.Where( p2 => pgr2.Key == p1.Key && p2.DataField == column.DataField && p2.ColumnText.Contains( requestValue ) ).Any() );
All of the properties of p1 and p2 are properties of a parent class of the element type of the IQueryable iQ. The Type variable I'd like to create will be the actual element type of iQ, i.e. the child class.
How do I do it?
Current Progress
Based on the answers below, I've written this test code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace IQueryableWhereTypeChange {
class Program {
static void Main( string[] args ) {
var ints = new List<ChildQueryElement>();
for( int i = 0; i < 10; i++ ) {
ints.Add( new ChildQueryElement() { Num = i, Value = i.ToString() } );
}
IQueryable<ChildQueryElement> theIQ = ints.AsQueryable();
Type type = typeof(ChildQueryElement);
var body = Expression.Constant(true);
var parameter = Expression.Parameter(type);
var delegateType = typeof(Func<,>).MakeGenericType(type, typeof(Boolean));
var lambda = Expression.Lambda( delegateType, body, parameter );
Console.WriteLine( lambda.GetType() );
dynamic copy = theIQ;
Type copyType1 = copy.GetType().GetGenericArguments()[ 0 ];
Type elementType1 = ((IQueryable)copy).ElementType;
Console.WriteLine( "copyType1 : " + copyType1.ToString() );
Console.WriteLine( "elementType1 : " + elementType1.ToString() );
copy = Queryable.Where( copy, lambda );
Type copyType2 = copy.GetType().GetGenericArguments()[ 0 ];
Type elementType2 = ((IQueryable)copy).ElementType;
Console.WriteLine( "copyType2 : " + copyType2.ToString() );
Console.WriteLine( "elementType2 : " + elementType2.ToString() );
}
}
public class ParentQueryElement {
public int Num { get; set; }
}
public class ChildQueryElement : ParentQueryElement {
public string Value { get; set; }
}
}
That program has this output:
System.Linq.Expressions.Expression`1[System.Func`2[IQueryableWhereTypeChange.ChildQueryElement,System.Boolean]]
copyType1 : IQueryableWhereTypeChange.ChildQueryElement
elementType1 : IQueryableWhereTypeChange.ChildQueryElement
Unhandled Exception:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
The best overloaded method match for
'System.Linq.Queryable.Where<IQueryableWhereTypeChange.ChildQueryElement>(
System.Linq.IQueryable<IQueryableWhereTypeChange.ChildQueryElement>,
System.Linq.Expressions.Expression<System.Func<IQueryableWhereTypeChange.ChildQueryElement,bool>>
)'
has some invalid arguments
at CallSite.Target(Closure , CallSite , Type , Object , LambdaExpression )
at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2)
at IQueryableWhereTypeChange.Program.Main(String[] args)
I'd like to get this working before I try and replicate my complicated predicate.
I find the exception rather baffling, as the output for lambda.GetType() matches the type in the exception almost exactly. The only difference is System.Boolean versus bool, but that shouldn't matter.
You need to create the appropriate delegate type, then pass that to the Expression.Lambda method. For example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
class Test
{
static void Main()
{
var type = typeof(string);
var body = Expression.Constant(true);
var parameter = Expression.Parameter(type);
var delegateType = typeof(Func<,>).MakeGenericType(type, typeof(bool));
dynamic lambda = Expression.Lambda(delegateType, body, parameter);
Console.WriteLine(lambda.GetType()); // Expression<string, bool>
}
}
Now of course your body would normally not just be a constant - but we don't know what you need to do there. From your edited question, it looks like you do have some statically typed knowledge of the type, otherwise you wouldn't be able to express that lambda expression. So either you need to build up the expression tree manually to be the equivalent of the lambda expression (using Expression.Property etc) or create one expression tree from what you know, and then use an expression tree visitor to adapt that to the actual type.
EDIT: Note that the type of lambda must be dynamic for it to work as the second argument to Queryable.Where, otherwise the execution-time version of the C# compiler will use the static type of the variable (which would just be LambdaExpression) to constrain overload resolution. You want it to match the actual Expression<TDelegate> type, which you can't express at compile-time, so you need to use dynamic.
You'll need to create an expression with a body set to the constant true and a parameter of the type of your type. There is an Expression to do each of these things:
Type type = typeof(object);
var lambda = Expression.Lambda(
Expression.Constant(true),
Expression.Parameter(type, "parameter"));
You cant pass the Type as ageneric parameter, this is not the idea of a generic, what you want is simply a parameter to a method for instance.

passing static reflection information to static generic methods

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);
}

Can Action.ToString() return anything other than "System.Action"?

I'm programming in Unity, using an Action event to hold a bunch of other Action delegates, in order to hook non-Monobehaviour objects into the Update() system. I'd like to be able to print the names of the actions to the Debug console, but using something like:
Delegate[] actions = updateActions.GetInvocationList();
foreach ( Delegate del in actions ) {
Debug.Log( del.ToString() );
}
... just returns "System.Action". I've tried also (del as Action).ToString() with no luck.
You can use the Method property to get a MethodInfo which should have a useful name.
Delegate[] actions = updateActions.GetInvocationList();
foreach ( Delegate del in actions )
{
Debug.Log( del.Method.ReflectedType.FullName + "." + del.Method.Name );
}
You can use del.Method.ToString() if you want the signature or del.Method.Name if you only want the name. del.Method.ReflectedType.FullName gives you the type name.
For lambdas/anonymous methods the name might not be too useful since they only have a compiler generated name. In the current implementation the name of a lambda is something like <Main>b__0 where Main is the name of the method that contains the lambda. Together with the type name this should give you a decent idea which lambda it is.
If you mean that you declare a delegate
var foo = new Action(() => { /* do something */ });
and you want to retrieve the word "foo" later, you're out of luck. To get that behavior you'll have to consume the declaration as an expression tree and parse out foo yourself.

Categories