Recursive call to generic function with DataRow parameter - MakeGenericMethod Fails - c#

the question is : if i have MyConvertDataRowToEntity(DataRow row )
and I call in with T object from type Parent and inside I call the same function with desendant type Child how should I pass the DataRow parameter ?
The problem is created when Invoke of MakeGenericMethod called.
Did change the type to DataSet , string and String types .
No luck.
(I recognize the children object bu prefix in column names - PrefixDataColumn )
public static T MyConvertDataRowToEntity<T>(DataRow row ) where T : class, new()
{
Type objType = typeof(T);
Type parentObjType = typeof(T);
T obj = Activator.CreateInstance<T>(); //hence the new() contsraint
PropertyInfo propertyGenericType = null;
object childInstance = null;
PropertyInfo property;
string childColumnName = string.Empty ;
foreach (DataColumn column in row.Table.Columns)
{
column.ColumnName = column.ColumnName.Replace("_", "");
string PrefixDataColumn;
if (column.ColumnName.IndexOf(".") > (-1))
{
///gets the prefix that is the same as child entity name
PrefixDataColumn = column.ColumnName.Substring(0, column.ColumnName.IndexOf("."));
///the column name in the child
int length = column.ColumnName.Length - 1;
int start = column.ColumnName.IndexOf(".") + 1;
childColumnName = column.ColumnName.Substring(column.ColumnName.IndexOf(".") + 1);
propertyGenericType = objType.GetProperty(PrefixDataColumn,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy);
parentObjType = objType;
if (propertyGenericType != null)
{
Type childType = propertyGenericType.PropertyType;
objType = childType;
childInstance = Activator.CreateInstance(propertyGenericType.PropertyType);
// get the get method for the property
MethodInfo method = propertyGenericType.GetGetMethod(true);
// get the generic get-method generator
MethodInfo genericHelper = typeof(DataUtil).GetMethod("MyConvertDataRowToEntity", BindingFlags.Public | BindingFlags.Static);
List<Type> signature = new List<Type>();
// first type parameter is type of target object
signature.Add(childType);
//next parameters are real types of method arguments
foreach (ParameterInfo pi in genericHelper.GetParameters())
{
signature.Add(pi.ParameterType);
}
// last parameters are known types of method arguments
signature.AddRange(typeof(T).GetGenericArguments());
// reflection call to the generic get-method generator to generate the type arguments
//MethodInfo constructedHelper = genericHelper.MakeGenericMethod(signature.ToArray());
// reflection call to the generic get-method generator to generate the type arguments
MethodInfo constructedHelper = genericHelper.MakeGenericMethod(childType );
// now call it. The null argument is because it's a static method.
object ret = constructedHelper.Invoke(null, new object[] { method });
// object myObj = method.Invoke(null, row);
//// property.SetValue(obj, MyConvertDataRowToEntity<childInstance>(DataRow row),null);
// childInstance = DataUtil.GetMethod("MyConvertDataRowToEntity").MakeGenericMethod(childType); //MyConvertDataRowToEntity<object>(row);
//childType initializedChild = ;
//property.SetValue(obj, value, null);
//objType = parentObjType;
}
else
{
continue;
}
}
}
return obj;
}
Getting this error :
Object of type 'System.Reflection.RuntimeMethodInfo' cannot be converted to type 'System.Data.DataRow'.
Is there any solution for this ?
p.s.
Narrowed down the code as much as i could.
How can I invoke the Method recursivly with desedant types and pass datarow ?

The reason for the error is because you are calling MyConvertDataRowToEntity<ChildType> but then passing in the getaccessor methodinfo for the property as the parameter instead of a data row containing only those fields.
If you want to continue with the code processing logic you are currently using you would need to construct a new datarow containing the fields you wanted (with the prefix and ".") removed from the start of the column names.
Alternatively you could create a helper method the accepted a column name, the source object and it simply updated the value.
static void UpdateItemProperty<T>(T item, string columnName, object rowValue) {
var prefixColumn=columnName.IndexOf(".")==-1 ? columnName : columnName.Split(".")[0];
var pi = typeof(T).GetProperty(prefixColumn // Add your binding flags);
// if pi==null then there is an error...
if (column.ColumnName.IndexOf(".") == (-1)) { // No Nesting
pi.SetValue(item,rowValue);
return;
}
// Nesting
var child=pi.GetValue(item);
if (child==null) {
// Logic here to get childs type and create an instance then call pi.SetValue with child
}
var remainder=string.Join(',',columnName.Split(".").Skip(1).ToArray());
// make your generic method info for UpdateItemProperty with pi.PropertyType into mi
mi.Invoke(null,new object[] { child,remainder,value };
}

Related

Activator.CreateInstance with string

I'm trying to populate a generic List< T > from another List< U > where the field names match, something like the untested pseudocode below. Where I'm having problems is when T is a string, for instance, which has no parameterless constructor. I've tried adding a string directly to the result object, but this gives me the obvious error -- that a string is not of Type T. Any ideas of how to solve this issue? Thanks for any pointers.
public static List<T> GetObjectList<T, U>(List<U> givenObjects)
{
var result = new List<T>();
//Get the two object types so we can compare them.
Type returnType = typeof(T);
PropertyInfo[] classFieldsOfReturnType = returnType.GetProperties(
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.NonPublic |
BindingFlags.Public);
Type givenType = typeof(U);
PropertyInfo[] classFieldsOfGivenType = givenType.GetProperties(
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.NonPublic |
BindingFlags.Public);
//Go through each object to extract values
foreach (var givenObject in givenObjects)
{
foreach (var field in classFieldsOfReturnType)
{
//Find where names match
var givenTypeField = classFieldsOfGivenType.Where(w => w.Name == field.Name).FirstOrDefault();
if (givenTypeField != null)
{
//Set the value of the given object to the return object
var instance = Activator.CreateInstance<T>();
var value = field.GetValue(givenObject);
PropertyInfo pi = returnType.GetProperty(field.Name);
pi.SetValue(instance, value);
result.Add(instance);
}
}
}
return result;
}
If T is string and you have already created custom code to convert your givenObject to a string, you just need to do an intermediate cast to object to add it to a List<T>:
public static List<T> GetObjectList2<T, U>(List<U> givenObjects) where T : class
{
var result = new List<T>();
if (typeof(T) == typeof(string))
{
foreach (var givenObject in givenObjects)
{
var instance = givenObject.ToString(); // Your custom conversion to string.
result.Add((T)(object)instance);
}
}
else
{
// Proceed as before
}
return result;
}
Incidentally, you are adding an instance of T to result for every property of T that matches a property name in U and for every item in givenObjects. I.e. if givenObjects is a list of length 1 and T is a class with 10 matching properties, result could end up with 10 entries. This looks wrong. Also, you need to watch out for indexed properties.
As an alternative to this approach, consider using Automapper, or serializing your List<U> to JSON with Json.NET then deserializing as a List<T>.

Field getter using Reflection.Emit Opcodes - usage of IL local variables

I'm learning IL and I thought of writing kind of a high-performance hack to access a field values of any object (like a reflection but faster).
So I made this class for testing:
public class CrashTestDummy
{
public int Number { get; set; }
public CrashTestDummy(int number)
{
Number = number;
}
public override string ToString()
{
return string.Format("CrashTestDummy: Number = {0}", Number);
}
}
Then I have such a program (I added comments after all IL instructions to improve readability, also divided into several logical parts; after every part there is written what I think is now on the stack):
class Program
{
static void Main(string[] args)
{
var backingFieldFormat = "<{0}>k__BackingField";
var getPropFormat = "get_{0}";
var dummy = new CrashTestDummy(5);
var t = dummy.GetType();
var f = t.GetField(string.Format(backingFieldFormat, "Number"),
BindingFlags.Instance | BindingFlags.NonPublic);
// define method: object Getter(Type, FieldInfo, Object), ignoring private fields.
var getter = new DynamicMethod("Getter", typeof(object), new Type[] { typeof(Type), typeof(FieldInfo), typeof(object) }, true);
var il = getter.GetILGenerator();
var _t = il.DeclareLocal(typeof(Type)); // Type _t;
var _f = il.DeclareLocal(typeof(FieldInfo)); // FieldInfo _f;
var _ft = il.DeclareLocal(typeof(Type)); // Type _ft;
var get_FieldType = typeof(FieldInfo).GetMethod(string.Format(getPropFormat, "FieldType")); // MethodInfo for FieldInfo.FieldType getter
var get_IsValueType = typeof(Type).GetMethod(string.Format(getPropFormat, "IsValueType")); // MethodInfo for Type.IsValueType getter
var lbl_NotValueType = il.DefineLabel(); // label "NotValueType"
// PART 1.
il.Emit(OpCodes.Ldarg_0); // Get argument 0 (type of object) ...
il.Emit(OpCodes.Castclass, typeof(Type)); // ... cast it to Type (just in case) ...
il.Emit(OpCodes.Stloc, _t); // ... and assign it to _t.
il.Emit(OpCodes.Ldarg_1); // Get argument 1 (desired field of object) ...
il.Emit(OpCodes.Castclass, typeof(FieldInfo)); // ... cast it to FieldInfo (just in case) ...
il.Emit(OpCodes.Stloc, _f); // ... and assign it to _f.
// stack is empty
// DEBUG PART
il.EmitWriteLine(_t); // these two lines show that both
il.EmitWriteLine(t.ToString()); // t and _t contains equal Type
il.EmitWriteLine(_f); // these two lines show that both
il.EmitWriteLine(f.ToString()); // f and _f contains equal FieldInfo
// stack is empty
// PART 2.
il.Emit(OpCodes.Ldarg_2); // Get argument 2 (object itself) ...
il.Emit(OpCodes.Castclass, _t); // ... cast it to type of object ...
il.Emit(OpCodes.Ldfld, _f); // ... and get it's desired field's value.
// desired field's value on the stack
// PART 3.
il.Emit(OpCodes.Ldloc, _f); // Get FieldInfo ...
il.Emit(OpCodes.Call, get_FieldType); // ... .FieldType ...
il.Emit(OpCodes.Call, get_IsValueType); // ... .IsValueType; ...
il.Emit(OpCodes.Brfalse, lbl_NotValueType); // ... IF it's false - goto NotValueType.
il.Emit(OpCodes.Ldloc, _f); // Get FieldInfo ...
il.Emit(OpCodes.Call, get_FieldType); // ... .FieldType ...
il.Emit(OpCodes.Stloc, _ft); // ... and assign it to _ft.
il.Emit(OpCodes.Box, _ft); // Box field's value of type _ft.
il.MarkLabel(lbl_NotValueType); // NotValueType:
// desired field's value on the stack (boxed, if it's value type)
// PART 4.
il.Emit(OpCodes.Ret); // return.
var value = getter.Invoke(null, new object[] { t, f, dummy });
Console.WriteLine(value);
Console.ReadKey();
}
}
This code crashes (on Invoke, and Exception from within Emit is as helpful as always). I can replace PARTs 2. and 3. as below:
// ALTERNATE PART 2.
il.Emit(OpCodes.Ldarg_2); // Get argument 2 (object itself) ...
il.Emit(OpCodes.Castclass, t); // ... cast it to type of object ...
il.Emit(OpCodes.Ldfld, f); // ... and get it's desired field's value.
// desired field's value on the stack
// ALTERNATE PART 3.
if (f.FieldType.IsValueType)
il.Emit(OpCodes.Box, f.FieldType); // Box field's value of type f.FieldType.
// desired field's value on the stack (boxed, if it's value type)
and it works fine. Notice that this time I'm not using any local variables, f and t are variables from outside of method. However with this approach I would need to generate as many methods, as number of types and fields it was being used to. So it's pretty unsatisfying solution.
I'm doing something wrong with local variables, apparently, but I was unable to figure out what it is exactly. What am I missing?
Edit:
Here's the code after big simplification. CrashTestDummy has now string property, so I could get rid of boxing the int:
public class CrashTestDummy
{
public string Text { get; set; }
public CrashTestDummy(string text)
{
Text = text;
}
}
And the main code is as follows:
static string BackingField(string propertyName)
{
return string.Format("<{0}>k__BackingField", propertyName);
}
static void Main(string[] args)
{
// INIT
var dummy = new CrashTestDummy("Loremipsum");
var t = typeof(CrashTestDummy);
var f = t.GetField(BackingField("Text"),
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic);
var fieldGetter = new DynamicMethod("FieldGetter", typeof(object), new Type[] { typeof(object) }, true);
var il = fieldGetter.GetILGenerator();
// DYNAMIC METHOD CODE
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, t);
il.Emit(OpCodes.Ldfld, f);
il.Emit(OpCodes.Ret);
var d = (Func<object, object>)fieldGetter.CreateDelegate(typeof(Func<object, object>));
// BENCHMARK
Stopwatch sw = new Stopwatch();
var len = 1000000;
for (int i = 0; i < len; i++)
{
sw.Start();
d(dummy);
sw.Stop();
}
Console.WriteLine(sw.Elapsed);
sw.Reset();
for (int i = 0; i < len; i++)
{
sw.Start();
f.GetValue(dummy);
sw.Stop();
}
Console.WriteLine(sw.Elapsed);
Console.ReadKey();
}
What you could do is create a specialized accessor function on the first use of any given FieldInfo. This removes reflection costs for subsequent accesses and replaces them with the cost of a delegate call which is much cheaper.
It's only about 2x faster than [reflection]
I would doubt this benchmark result. How could it be faster? If you generate the same code in IL that the C# compiler would have generated you will get the same level of performance. No point in doing that.

Checking for Nulls on DB Record Mapping

How can I check for db null values in the attached code? Please understand I am a new C# convert...
What this code does is takes a IDataReader object and converts and maps it to a strongly-typed list of objects. But what I am finding is it completely errors out when there are null columns returned in the reader.
Converter
internal class Converter<T> where T : new()
{
// Declare our _converter delegate
readonly Func<IDataReader, T> _converter;
// Declare our internal dataReader
readonly IDataReader dataReader;
// Build our mapping based on the properties in the class/type we've passed in to the class
private Func<IDataReader, T> GetMapFunc()
{
// declare our field count
int _fc = dataReader.FieldCount;
// declare our expression list
List<Expression> exps = new List<Expression>();
// build our parameters for the expression tree
ParameterExpression paramExp = Expression.Parameter(typeof(IDataRecord), "o7thDR");
ParameterExpression targetExp = Expression.Variable(typeof(T));
// Add our expression tree assignment to the exp list
exps.Add(Expression.Assign(targetExp, Expression.New(targetExp.Type)));
//does int based lookup
PropertyInfo indexerInfo = typeof(IDataRecord).GetProperty("Item", new[] { typeof(int) });
// grab a collection of column names from our data reader
var columnNames = Enumerable.Range(0, _fc).Select(i => new { i, name = dataReader.GetName(i) }).AsParallel();
// loop through all our columns and map them properly
foreach (var column in columnNames)
{
// grab our column property
PropertyInfo property = targetExp.Type.GetProperty(column.name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
// check if it's null or not
if (property != null)
{
// build our expression tree to map the column to the T
ConstantExpression columnNameExp = Expression.Constant(column.i);
IndexExpression propertyExp = Expression.MakeIndex(paramExp, indexerInfo, new[] { columnNameExp });
UnaryExpression convertExp = Expression.Convert(propertyExp, property.PropertyType);
BinaryExpression bindExp = Expression.Assign(Expression.Property(targetExp, property), convertExp);
// add it to our expression list
exps.Add(bindExp);
}
}
// add the originating map to our expression list
exps.Add(targetExp);
// return a compiled cached map
return Expression.Lambda<Func<IDataReader, T>>(Expression.Block(new[] { targetExp }, exps), paramExp).Compile();
}
// initialize
internal Converter(IDataReader dataReader)
{
// initialize the internal datareader
this.dataReader = dataReader;
// build our map
_converter = GetMapFunc();
}
// create and map each column to it's respective object
internal T CreateItemFromRow()
{
return _converter(dataReader);
}
}
Mapper
private static IList<T> Map<T>(DbDataReader dr) where T : new()
{
try
{
// initialize our returnable list
List<T> list = new List<T>();
// fire up the lamda mapping
var converter = new Converter<T>(dr);
while (dr.Read())
{
// read in each row, and properly map it to our T object
var obj = converter.CreateItemFromRow();
// add it to our list
list.Add(obj);
}
// reutrn it
return list;
}
catch (Exception ex)
{
// make sure this method returns a default List
return default(List<T>);
}
}
I just don't quite understand where the column to typed object happens in here, so I'd try to do it myself... but I just don;t know where it is.
I know this probably won't help much, but the error I am getting is:
Unable to cast object of type 'System.DBNull' to type 'System.String'.
and it happens on the
internal T CreateItemFromRow()
{
return _converter(dataReader); //<-- Here
}
Note
This does not happen if I wrap the columns in the query itself with an ISNULL(column, ''), but I am sure you can understand that this is surely not a solution
The problem lies in the line convertExp = Expression.Convert(propertyExp, property.PropertyType). You can't expect to convert DbNull value to its equivalent in framework type. This is especially nasty when your type is a value type. One option is to check if the read value from db is DbNull.Value and in case yes, you need to find a compatible value yourself. In some cases people are ok with default values of those types in C#. If you have to do this
property = value == DBNull.Value ? default(T): value;
a generic implementation would look like (as far as the foreach in your converter class goes):
foreach (var column in columns)
{
var property = targetExp.Type.GetProperty(
column.name,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (property == null)
continue;
var columnIndexExp = Expression.Constant(column.i);
var propertyExp = Expression.MakeIndex(
paramExp, indexerInfo, new[] { columnIndexExp });
var convertExp = Expression.Condition(
Expression.Equal(
propertyExp,
Expression.Constant(DBNull.Value)),
Expression.Default(property.PropertyType),
Expression.Convert(propertyExp, property.PropertyType));
var bindExp = Expression.Assign(
Expression.Property(targetExp, property), convertExp);
exps.Add(bindExp);
}
Now this does an equivalent of
property = reader[index] == DBNull.Value ? default(T): reader[index];
You could avoid the double lookup of the reader by assigning it to a variable and using its value in the conditional check. So this should be marginally better, but a lil' more complex:
foreach (var column in columns)
{
var property = targetExp.Type.GetProperty(
column.name,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (property == null)
continue;
var columnIndexExp = Expression.Constant(column.i);
var cellExp = Expression.MakeIndex(
paramExp, indexerInfo, new[] { columnIndexExp });
var cellValueExp = Expression.Variable(typeof(object), "o7thPropValue");
var convertExp = Expression.Condition(
Expression.Equal(
cellValueExp,
Expression.Constant(DBNull.Value)),
Expression.Default(property.PropertyType),
Expression.Convert(cellValueExp, property.PropertyType));
var cellValueReadExp = Expression.Block(new[] { cellValueExp },
Expression.Assign(cellValueExp, cellExp), convertExp);
var bindExp = Expression.Assign(
Expression.Property(targetExp, property), cellValueReadExp);
exps.Add(bindExp);
}
This does the conditional check this way:
value = reader[index];
property = value == DBNull.Value ? default(T): value;
This is one of the most annoying problems in dealing with datasets in general.
The way I normally get around it is to convert the DBNull value to something more useful, like an actual null or even a blank string in some cases. This can be done in a number of ways, but just recently I've taken to using extension methods.
public static T? GetValueOrNull<T>(this object value) where T : struct
{
return value == null || value == DBNull.Value ? (T?) null : (T) Convert.ChangeType(value, typeof (T));
}
A handy extension method for nullable types, so for example:
int? myInt = DataSet.Tables[0].Rows[0]["DBNullInt"].GetValueOrNull<int>();
Or a more generic one to just convert a DBNull in to a null:
public static object GetValueOrNull(this object value)
{
return value == DBNull.Value ? null : value;
}
string myString DataSet.Tables[0].Rows[0]["DBNullString"].GetValueOrNull();
You'll then get a null string, rather than trying to put a DBNull in to a string.
Hopefully that may help you a little.
As I come across this problem recently
both
Expression.TypeIs(propertyExp,typeof(DBNull));
and
Expression.Equal(propertyExp,Expression.Constant(DBNull.Value));
didn't work for me as they did increase memory allocation (which is my primary concern in this case)
here is the benchmark for both mapper approach compare to Dapper on 10K rows query.
TypeIs
and
Equal
so to fix this problem it came out that an IDataRecord is able to call "IsDBNull" to check whether the column in current reader is DBNull or not
and can be write as expression like
var isReaderDbNull = Expression.Call(paramExp, "IsDBNull", null, readerIndex);
finally, I end up with this solution
and now the performance is acceptable again.

I need to get the value from Nested Property

I need to get the value from nested property.
In MainClass i have a Students Property which is Type of Student Class.
MainClass obj = new MainClass ();
obj.Name = "XII";
obj.Students = new List<Students>()
{
new Students() { ID = "a0", Name = "A" },
new Bridge() { ID = "a1", Name = "B" }
};
Type t = Type.GetType(obj.ToString());
PropertyInfo p = t.GetProperty("Students");
object gg = p.GetValue(obj, null);
var hh = gg.GetType();
string propertyType = string.Empty;
if (hh.IsGenericType)
{
string[] propertyTypes = hh.ToString().Split('[');
Type gggg = Type.GetType(propertyTypes[1].Remove(propertyTypes[1].Length - 1));
foreach (PropertyInfo pro in gggg.GetProperties())
{
if (pro.Name.Equals("Name"))
{
// Get the value here
}
}
}
First of all,
Type t = Type.GetType(obj.ToString());
is wrong. This only works if a class has not overloaded the ToString() method, and will fall at runtime if it has. Fortunately, every class has a GetType() metod (it's defined on object, so Type t = obj.GetType() is the correct code.
Second, the
string[] propertyTypes = hh.ToString().Split('[');
Type gggg = Type.GetType(propertyTypes[1].Remove(propertyTypes[1].Length - 1)
is an awful way to get the type the specified the generic type, as there is a method GetGenericArguments that does that for you, so this code could be changed with
Type[] genericArguments = hh.GetGenericArguments();
Type gggg = genericArguments[0];
And now to the real problem, accessing an list item. The best way to do that is to use the indexer ([]) of the List<> class. In c# when you define an indexer, it's automatically transfered to a property called Item, and we can use that property to extract our values (the Item name can be inferred from the type definition, but for the most part, that can be hardcoded)
PropertyInfo indexer = hh.GetProperty("Item"); // gets the indexer
//note the second parameter, that's the index
object student1 = indexer.GetValue(gg, new object[]{0});
object student2 = indexer.GetValue(gg, new object[]{1});
PropertyInfo name = gggg.GetProperty("Name");
object studentsName1 = name.GetValue(student1, null); // returns "A"
object studentsName2 = name.GetValue(student2, null); // returns "B"
Override Equals method for both Students and Bridge class then use Find or use LINQ

Reflection on a static overloaded method using an out parameter

I'm having some issues with invoking an overloaded static method with an out parameter via reflection and would appreciate some pointers.
I'm looking to dynamically create a type like System.Int32 or System.Decimal, and then invoke the static TryParse(string, out x) method on it.
The below code has two issues:
t.GetMethod("TryParse", new Type[] { typeof(string), t } ) fails to return the MethodInfo I expect
mi.Invoke(null, new object[] { value.ToString(), concreteInstance }) appears to succeed but doesn't set the out param concreteInstance to the parsed value
Interwoven into this function you can see some temporary code demonstrating what should happen if the type parameter was set to System.Decimal.
public static object Cast(object value, string type)
{
Type t = Type.GetType(type);
if (t != null)
{
object concreteInstance = Activator.CreateInstance(t);
decimal tempInstance = 0;
List<MethodInfo> l = new List<MethodInfo>(t.GetMethods(BindingFlags.Static | BindingFlags.Public));
MethodInfo mi;
mi = t.GetMethod("TryParse", new Type[] { typeof(string), t } ); //this FAILS to get the method, returns null
mi = l.FirstOrDefault(x => x.Name == "TryParse" && x.GetParameters().Length == 2); //ugly hack required because the previous line failed
if (mi != null)
{
try
{
bool retVal = decimal.TryParse(value.ToString(), out tempInstance);
Console.WriteLine(retVal.ToString()); //retVal is true, tempInstance is correctly set
object z = mi.Invoke(null, new object[] { value.ToString(), concreteInstance });
Console.WriteLine(z.ToString()); //z is true, but concreteInstance is NOT set
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
return concreteInstance;
}
return value;
}
What do I need to do to ensure that my t.GetMethod() call returns the correct MethodInfo? What do I need to do to have concreteInstance correctly set in my mi.Invoke() call?
I know there are a bunch of questions on this topic, but most of them involve static generic methods or static methods that are not overloaded. This question is similar but not a duplicate.
You need to use the right BindingFlags and use Type.MakeByRefType for out and ref parameters. One second, and I'll have a code sample for you.
For example,
MethodInfo methodInfo = typeof(int).GetMethod(
"TryParse",
BindingFlags.Public | BindingFlags.Static,
Type.DefaultBinder,
new[] { typeof(string), typeof(int).MakeByRefType() },
null
);
I should point out that invoking this is a little tricky too. Here's how you do it.
string s = "123";
var inputParameters = new object[] { "123", null };
methodInfo.Invoke(null, inputParameters);
Console.WriteLine((int)inputParameters[1]);
The first null is because we are invoking a static method (there is no object "receiving" this invocation). The null in inputParameters will be "filled" for us by TryParse with the result of the parse (it's the out parameter).

Categories