How shall I call "MyMethod" using reflection in below code.
I have an existing C# code which has predefined structure which I am not allow to change. I need to call a method present in a class using reflection.
In below code "_instance" contains object of "Foo". I neeed to call "MyMethod" using "PropElementHighlighter" property in Consumer class.
using System.Reflection;
public class Foo
{
public void MyMethod(string Argument)
{
//some code
}
}
public class MainWindow
{
private Foo _instance;
public Foo PropElementHighlighter { get { return _instance; } }
}
public class Consumer
{
Type control = MainWindow.GetType();
PropertyInfo l_propInfo = control.GetProperty("PropElementHighlighter", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
MethodInfo l_HighlightMethodInfo = l_propInfo.PropertyType.GetMethod("MyMethod");
l_HighlightMethodInfo.Invoke(l_propInfo, new object[]{"Parameter1"});
}
I am getting error "Object does not match target type." while invoking method.
You are getting error because you are setting property info in object of method. Try to set value of property:
Type control = mainWindow.GetType();
PropertyInfo l_propInfo = control.GetProperty("PropElementHighlighter", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
var propertyValue = l_propInfo.GetValue(mainWindow);
MethodInfo l_HighlightMethodInfo = l_propInfo.PropertyType.GetMethod("MyMethod");
l_HighlightMethodInfo.Invoke(propertyValue, new object[] { "Parameter1" });
Related
I have an app that takes the dll of an external app, look into it for a specified class and method. It then gets the methodinfo from this external method and tries to then Create a delegate via Delegate.CreateDelegate
I constantly get
System.ArgumentException: 'Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.'
I have gone and extracted the code a bit to make it easier to share and debug as well as write a small simple external app to read from. See the code below:
External App Example as Library (.Net Framework 4.8)
using System;
namespace MethodLib
{
public class PrintText
{
public string Print(string textToPrint, int number)
{
return $"{ PrintPrivate(textToPrint) }: {number}";
}
public static string PrintStatic(string textToPrint)
{
return textToPrint;
}
public void PrintVoid(string textToPrint)
{
Console.WriteLine(textToPrint);
}
private string PrintPrivate(string textToPrint)
{
return $"This is { textToPrint }";
}
}
}
App to CreateDelegate
MethodInfo Creation
using System;
using System.Reflection;
namespace DelegateApp
{
public class PluginSupport
{
public MethodInfo GetMethodInfo(string methodName, string externalLocation)
{
var instance = Activator.CreateInstance(Assembly.LoadFrom(externalLocation)
.GetType("MethodLib.PrintText"));
var methodInfo = instance.GetType()
.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
return methodInfo;
}
}
}
Create Delegate Part
namespace DelegateApp
{
public class MethodGenerator
{
private PluginSupport _pluginSupport;
public MethodGenerator()
{
_pluginSupport = new PluginSupport();
}
public MethodDetails Create(string methodName, string path)
{
var method = _pluginSupport.GetMethodInfo(methodName, path);
if (Equals(method, null))
{
throw new KeyNotFoundException($"Method '{ methodName }' doesn't exist in class");
}
return new MethodDetails
{
MethodName = method.Name,
ComponentName = method.DeclaringType.Name,
FriendlyName = method.DeclaringType.Name,
Parameters = method.GetParameters(),
LogicalPath = method.DeclaringType.Assembly.Location,
Method = (Func<string>)Delegate.CreateDelegate(typeof(Func<string>), method)
};
}
}
}
What have I tried
So reading a lot of different post I gather that the call I am using
(Func<string>)Delegate.CreateDelegate(typeof(Func<string>), method) is actually meant for static methods only, and as I am interested in all the public methods I am missing a target/instance.
So from other examples, you need to create the instance and pass that in as well, so I used the var myInstance = Actovator.CreateInstance and then passed this variable in as well, ending up with the following
(Func<string>)Delegate.CreateDelegate(typeof(Func<string>), myInstance, method)
I have also tried to use this one
public static Delegate CreateDelegate(Type type, Type target, string method, bool ignoreCase);
All of this keeps throwing
System.ArgumentException: 'Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.'
The only time I get it to work, is when I do the following:
methodName = PrintStatic from external app
var methodInfo = instance.GetType()
.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
var deleg = (Func<string>)Delegate.CreateDelegate(typeof(Func<string>),null, method)
of course this is not what I want as this only does the static for me and I want the non-static as well. But even with this if I add BindingFlags.Instance to the mix the static will also throw the same error.
If I also remove BindingFlags.Instance and my methodName = Print, then methodInfo is null.
My Questions
What am I not understanding/missing with regards to the Delegate.CreateDelegate?
What code am I missing that this is not working as I am expecting?
Is there a different way to do the same thing?
From creating the Delegate I want to invoke it later in the code, but is there a penalty for just using the invoke directly on methodinfo instead of creating a delegate then invoking it?
Why does methodinfo not give me my public non-static member if BindingFlags.Instance is omitted?
Thanks to #Charlieface, I realised my signature types were not corresponding to me creating the delegate.
So what I finally ended up with in this example code was to do the following in MethodGenerator class
Get the parameters from methodinfo
Go through the params and add them to a list of Types and get the type of each param
Build a func where I do not know the number of types it will need and replace the number with the amount of params I have from methodinfo + output type
Check if method isstatic and based on this set it to
methHead = method.IsStatic
? Delegate.CreateDelegate(delegateFunc.MakeGenericType(types.ToArray()), null, method)
: Delegate.CreateDelegate(delegateFunc.MakeGenericType(types.ToArray()), instance, method);
This is a bit of elaborate code I guess, but it works and will need to refine it or drop it in the actual code base where we want to use it. But as #Charlieface mentioned if you don't know the type, there isn't much point to the delegate.
Final piece of code
public MethodDetails Create(string methodName, string path)
{
var method = _pluginSupport.GetMethodInfo(methodName, path);
if (Equals(method, null))
{
throw new KeyNotFoundException($"Method '{ methodName }' doesn't exist in class");
}
var instance = Activator.CreateInstance(method.DeclaringType);
List<Type> types = new List<Type>();
var methodPrams = method.GetParameters();
foreach (var item in methodPrams)
{
types.Add(Type.GetType(item.ParameterType.UnderlyingSystemType.FullName));
}
var funcType = typeof(Func<>);
var delegateFunc = Type.GetType(funcType.FullName.Replace("1", (methodPrams.Length + 1).ToString()));
Delegate methHead;
types.Add(typeof(string));
methHead = method.IsStatic
? Delegate.CreateDelegate(delegateFunc.MakeGenericType(types.ToArray()), null, method)
: Delegate.CreateDelegate(delegateFunc.MakeGenericType(types.ToArray()), instance, method);
return new MethodDetails
{
MethodName = method.Name,
ComponentName = method.DeclaringType.Name,
FriendlyName = method.DeclaringType.Name,
Parameters = method.GetParameters(),
LogicalPath = method.DeclaringType.Assembly.Location,
Method = methHead
};
}
I have A class:
public abstract class A
{
}
And then I have B class that derives from it:
public sealed class B : A
{
public void SomeMethod()
{
var method = this.GetType().GetMethod("AddText");
}
private void AddText(string text)
{
...
}
}
Why is GetMethod returning null?
var methodInfo = this.GetType().GetMethod("AddText", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(string) }, null);
Your method has a parameter, you need to use the overload that accepts a type array for the parameter types and the binding flags.
In .net Method signatures are based on their name, their return type, and their parameters.
So if your method has parameters you have to tell Reflection what parameter types it has via a Type[].
By default, Reflection will only search for public methods.
You need to pass BindingFlags.Instance | BindingFlags.NonPublic.
Question is simple: I'm using reflection to get a value. Then if it's a struct, I'm calling a method FooStruct, else FooClass:
Type type = x.GetType();
foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
var val = fieldInfo.GetValue(value);
object obj = type.IsValueType ? val.FooStruct() : val.FooClass();
fieldInfo.SetValue(x, obj);
}
problem is that FooStruct has a constraint:
public static T FooStruct<T>(this T value) where T : struct
{
//...
}
so question is: is it possible to call a method with struct constraint for an object which contains a boxed struct instance without reflection?
I'd happily be proven wrong by another answer, but I don't think this is possible without resorting even more to reflection. See further below for the reason that makes me suspect this. See end of the answer for a reflection-based solution.
Practical suggestion: I would simply drop the constraint on your FooStruct and FooClass methods, and additionally:
either make them non-generic and accept an argument of type object (which is what val is declared as, anyway). There's no advantage to having these methods be generic if they are only ever passed objects;
or cast val from object to T before invoking FooStruct / FooClass.
Why does it seem impossible to do what you're asking? You are trying to convert an expression that is statically typed object (namely val) into something that is statically typed <T> where T : struct or <T> where T : class (in order to call the respective extension method on such a T). That is, you are trying to dynamically introduce a new type variable inside your foreach loop. Unfortunately, the only way to introduce a type variable is to declare it in advance, i.e. as some generic type parameter T in the method's signature; and then it is not the code inside your method that gets to choose what actual type it stands for—it's the calling code that determines T.
Reflection-based solution:
// determine which method ought to be called based on `val`'s run-time type.
// (for C# 6 and later, use the `nameof` operator instead of hard-coding method names)
Type type = val.GetType();
string fooName = type.IsValueType ? "FooStruct" : "FooClass";
// bind to the generic method and supply the type argument for it:
// (I'm assuming that your extension methods are defined in `FooMethodsClass`.)
MethodInfo fooOpen = typeof(FooMethodsClass).GetMethod(fooName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
MethodInfo foo = fooOpen.MakeGenericMethod(new Type[] { type });
// invoke the generic (extension) method with `val` as the `this` argument:
foo.Invoke(null, new object[] { val });
The dynamic variable support will set T appropriately. I use this trick regularly. Try it like this:
Type type = x.GetType();
foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
dynamic val = fieldInfo.GetValue(value);
object obj = type.IsValueType ? Utilities.FooStruct(val) : Utilities.FooClass(val);
fieldInfo.SetValue(x, obj);
}
Apparently you can call the methods with reflection and they work without a problem:
using System;
using System.Reflection;
namespace DemoDynamicT
{
public static class Utilities
{
public static T FooStruct<T>(this T value) where T:struct
{
return default(T);
}
public static T FooClass<T>(this T value) where T : class
{
return default(T);
}
}
public class Program
{
class TestClass
{
public TestStruct StructField;
}
struct TestStruct
{
public int x;
int y;
}
public static void Main()
{
var x = new TestClass();
Type type = x.GetType();
foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
var val = fieldInfo.GetValue(x);
var methodInfo = typeof(Utilities).GetMethod(fieldInfo.FieldType.IsValueType ? "FooStruct" : "FooClass");
var toBeCalled = methodInfo.MakeGenericMethod(fieldInfo.FieldType);
object obj = toBeCalled.Invoke(null, new [] {val});
fieldInfo.SetValue(x, obj);
}
}
}
}
I don't think you can do this directly. You can try workaround like this:
public static class Utilities
{
public static ValueType FooStruct(this ValueType value)
{
//put your code here
return default(ValueType);
}
public static object FooClass(this object value)
{
//put your code here
return null;
}
public static T FooStruct<T>(this T value) where T: struct
{
return (T) FooStruct(value);
}
public static T FooClass<T>(this T value) where T: class
{
return (T) FooClass(value);
}
}
public class Program
{
class TestClass
{
public TestStruct StructField;
}
struct TestStruct
{
int x;
int y;
}
public static void Main()
{
var x = new TestClass();
Type type = x.GetType();
foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
var val = fieldInfo.GetValue(x);
object obj = fieldInfo.FieldType.IsValueType ? ((ValueType)val).FooStruct() : val.FooClass();
fieldInfo.SetValue(x, obj);
}
//Generic call
var structVar = new TestStruct();
structVar.FooStruct();
}
}
I have the following C# Code. A Base class and classes that inherit from that base class.
I use this Baseclasses in an special List. This List also has the Member ReadListAsXmlAs.
public class ResultSetBase
{
some Members
}
public class ResultSetBaseSweep : ResultSetBase
{
some other Members
}
public class ResultList<T> where T : ResultSetBase
{
public ResultList<T> ReadListAsXmlAs(params string[] path)
{
...
}
}
In an other methode I want to create an dynamic object of the Type ResultList. I know of which class the ResultList is, only at runtime. (e.g. ResulstSetBaseSweep, or any other inherited from ResultSetBase).
I create an dynamic Object of this Type. the following way.
Type myType = Type.GetType("Class in String Format");
Type listtype = typeof(ResultSaver.ResultList<>).MakeGenericType(myType);
object resultlist = Activator.CreateInstance(listtype);
Now i need to call the Methode ReadListAsXmlAs. As it is of type object, the compiler complains when when i try to call
resultlist.ReadListAsXmlAs(...);
So I tried to call it over Reflections:
myType.InvokeMember("ReadListAsXmlAs", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, resultlist, new object[] { filenames.ToArray() });
Then I get the compiler error: ReadListAsXmlAs not found! How is it done right?
I have found a solution to the problem:
Type myType = Type.GetType(LstBoxClass.SelectedItem.ToString());
Type listtype = typeof(ResultSaver.ResultList<>).MakeGenericType(myType);
object resultlist = Activator.CreateInstance(listtype);
MethodInfo method = listtype.GetMethod("ReadListAsXmlAs");
method.Invoke(resultlist, new Object[] {filenames.ToArray()});
I tried code below in .NET 3.5 but mi is null.
How to call private generic method so that type parameter can be passed at runtime?
If SaveEntityGeneric is marked as public this code works OK but I dont wat to make it public since it is only used in other method in same class to pass this class type using GetType().
using System.Reflection;
public class Main1
{
static void Main()
{
new Class1().Test();
}
}
class Class1
{
public void Test()
{
var mi = GetType().GetMethod("SaveEntityGeneric", BindingFlags.NonPublic);
// why mi is null ?
var gm = mi.MakeGenericMethod(GetType());
gm.Invoke(this, null);
}
void SaveEntityGeneric<TEntity>()
{
}
}
The binding flags are tricky to get right on this. Use BindingFlags.NonPublic | BindingFlags.Instance.
var mi = GetType().GetMethod("SaveEntityGeneric",
BindingFlags.NonPublic | BindingFlags.Instance);
var gm = mi.MakeGenericMethod(GetType());
gm.Invoke(this, null);
Simply make it internal, that will prevent from usage that method outside the assembly