Let's say that I have the following delegate:
public delegate void Example();
and a class such as the following:
public class TestClass {
Example FailingTest = () => Assert.Equal(0,1);
}
How can I use reflection to get the name "FailingTest"?
So far I have tried:
var possibleFields = typeof(TestClass).GetFields(relevant_binding_flags)
.Where(x => x.FieldType.Equals(typeof(Example)));
foreach(FieldInfo oneField in possibleFields) {
// HERE I am able to access the declaring type name
var className = oneField.ReflectedType.Name; // == "TestClass"
// but I am not able to access the field
// name "FailingTest" because:
var fieldName = oneField.Name; // == "CS$<>9__CachedAnonymousMethodDelegate1"
}
Stepping through in the debugger, I am unable to find a path to the name of the declared field, "FailingTest".
Is that info retained at runtime or is it lost when the anonymous delegate is assigned?
What BindingFlags are you passing to GetFields? I used these:
BindingFlags.NonPublic | BindingFlags.Instance
and I was able to see the name of the field.
Related
I'm trying to mod Planetbase and I have to access protected static List<Character> mCharacters = new List<Character>(); inside public abstract class Character in Planetbase namespace. Here is my code:
FieldInfo characters = typeof(Character).GetField("mCharacters", BindingFlags.NonPublic | BindingFlags.Instance);
carriedResources = Character.characters.Where(x => x.getLoadedResource() != null)
.ToDictionary(y => y, x => x.getLoadedResource()); // Get all carried resources across all characters
However I'm getting the "Character does not contain the definition for characters" error despite writing a method to access it. No idea what I'm doing wrong.
The BindingFlags you are using are not correct. What you need is BindingFlags.NonPublic | BindingFlags.Static. You can find out more about this enumeration here.
These are some of the most used binding flags when one would search for any type member (field, property, method, etc.):
BindingFlags.Public specifies that public members are to be included in the search.
BindingFlags.NonPublic specifies that non-public members are to be included in the search.
BindingFlags.Instance specifies that instance members are to be included in the search.
BindingFlags.Static specifies that static members are to be included in the search.
Update:
I just noticed that you are not accessing the field information appropriately. After you have the field info, you can access the underlying value like this:
FieldInfo field = typeof(Character).GetField("mCharacters", BindingFlags.NonPublic | BindingFlags.Instance);
var value = field.GetValue(__character_instance__);
The retrieved value will be of type object so you have to safely convert it to the appropriate type.
So if I have this class:
public class Person
{
private readonly string _name;
public Person(string name)
{
this._name = name;
}
}
I can access the field data like this:
var person = new Person("Tony Troeff");
var field = typeof(Person).GetField("_name", BindingFlags.NonPublic | BindingFlags.Instance);
var fieldValue = field.GetValue(person) as string;
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 generic class and a property of type Action<,>. I am wondering if there is a way to actually invoke this delegate using reflection in runtime, not just set value to this class property (via PropertyInfo.SetValue).
I tried a lot of things, like using expressions, dummy casting, read forums, but none of the solutions worked for me.
Workaround:
What I could think of is creating a dummy method which internally calls the delegate, and with reflection is fairly easy to invoke this method.
public class Student
{
public string Name { get; set; }
}
public class ConfigData<T>
where T: class
{
public Action<T, object> ValueInjector { get; set; }
public void SetValue(T entity, object valueToSet)
{
this.ValueInjector(entity, valueToSet);
}
}
class Program
{
static void Main(string[] args)
{
var configObj = new ConfigData<Student>()
{
ValueInjector = (x, y) =>
{
// Some custom logic here
x.Name = y.ToString();
}
};
// Parameters
Student student = new Student();
object valueToSet = "Test";
Type configType = configObj.GetType();
PropertyInfo propertyInfo = configType.GetProperty("ValueInjector");
// Invoke the property info somehow with the parameters ?
// Workarround - invoke a dummy method instead
MethodInfo methodInfo = configType.GetMethod("SetValue");
methodInfo.Invoke(configObj, new object[] { student, valueToSet });
Console.WriteLine(student.Name);
}
}
I want to be able to invoke the propertyInfo variable and pass to it the two parameters I already have (student, valueToSet), since I know that it represent a delegate which can be run.
Update:
I tried with castings as suggested by #HimBromBeere.
//Error in runtime
var del = (Action)propertyInfo.GetValue(configObj, null);
//Error in runtime
var del = (Action<object, object>)propertyInfo.GetValue(configObj, null);
// Works but no generic
var del = (Action<Student, object>)propertyInfo.GetValue(configObj, null);
del.Invoke(student, valueToSet);
Only the last casting works and I am able to call Invoke on the delegate (no need of DynamicInvoke) and it works. However this is not a solution because I do not know the exact type to cast in runtime. I have it as variable T. Something like:
var del = (Action<T, object>)propertyInfo.GetValue(configObj, null);
So maybe if I manage to make a generic Type like this:
var d1 = typeof(Action<,>);
Type[] typeArgs = { propertyInfo.DeclaringType.GenericTypeArguments[0], typeof(object) };
Type delegateType = d1.MakeGenericType(typeArgs);
there might be a way to do this conversion and execute. Still wondering.
You can cast the value returned from the property back to a delegate, e.g:
var del = (Action)propertyInfo.GetValue(configObj, null);
Now call the delegate with your params:
del.DynamicInvoke(student, valueToset)
I have a static class which looks for an implementation of an abstract type and stores it as a static property
similar to the below:
public static class MyStaticClass
{
private static MyAbstractType _MyAbstractImplementation;
public static MyAbstractType MyAbstractImplementation
{
get => _MyAbstractImplementation ?? ( _MyAbstractImplementation = FindImplementation());
private set => _MyAbstractImplementation = value;
}
}
And I am trying to call a method of MyAbstractImplementation (which contains no static properties or methods) via reflection:
var myAssembly = Assembly.Load("MyStaticClassAssembly")
var myType = myAssembly.GetTypes().First(t => t.Name == "MyAbstractType");
var myImplementation = myType.GetProperties()
.First(p=>p.ReflectedType?.Name == "MyAbstractImplementation")
.GetValue(null);
var obj = myType.GetMethod("SomeMethod")?.Invoke(
null,
new object[]
{
// Some args
});
The above code causes the following exception on getting the value of MyAbstractImplementation:
System.Reflection.TargetException: Non-static method requires a target.
Evidently, this is because I am passing null to GetValue(), so I try passing myAssembly rather than null and I get the following exception:
System.Reflection.TargetException: Object does not match target type.
Out of desperation, I try passing myType and myImplementation but I still get the same exception.
What am I meant to be passing to GetValue()?
From the error you are getting MyAbstractImplementation is not a static property, so it needs an instance to be run on. You are basically trying to write the following code:
new MyAbstractType().MyAbstractImplementation.SomeMethod();
Both the property and method access need the target to run against (the left side of '.'). So you need an instance of myType. Also the method will need the instance, which is the result of getting the property (myImplementation).
var myAssembly = Assembly.Load("MyStaticClassAssembly");
var myType = myAssembly.GetTypes().First(t => t.Name == "MyAbstractType");
var myTypeInstance = Activator.CreateInstance(myType); // Asuming has a default constructor
var myImplementation = myType.GetProperties()
.First(p => p.ReflectedType?.Name == "MyAbstractImplementation")
.GetValue(myTypeInstance);
var obj = myType.GetMethod("SomeMethod")?.Invoke(
myImplementation,
new object[]
{
// Some args
});
From how you write the code myImplementation should also be of type myType (myType.GetMethod("SomeMethod")) if it isn't replace it with: myImplementation.GetType().GetMethod("SomeMethod").
Basically, some internal check that happens in the static System.Linq.Expressions.Expression.Bind() method says "Method is not a property accessor" on my property that is clearly a property. Using Reflector, I've minimized the amount of code causing the problem, and I can't for the life of me figure out why this would happen. My only guess is that it has something to do with the fact that the property isn't on the class itself, but I would think this should still work:
I've tried to make a small example with the least amount of code possible. The code below should run in its entirety.
using System;
using System.Reflection;
public class Base
{
public virtual int Id { get; set; }
}
// As you can see, SubClass does not override the Id property of Base.
public class SubClass : Base { }
class Program
{
static void Main(string[] args)
{
// Getting the property directly from the type.
PropertyInfo propertyInfo = typeof(SubClass).GetProperty("Id");
MethodInfo setMethod = propertyInfo.GetSetMethod();
/* Code from here on out is from the System.Linq.Expressions.Bind() method (the one
that accepts a MethodInfo argument). This method causes GetProperty to be called
and retrieve what should be the same PropertyInfo. It fails here, saying something
along the lines of "Method is not a property accessor." which doesn't make sense. */
PropertyInfo propertyInfo2 = GetProperty(setMethod);
}
private static PropertyInfo GetProperty(MethodInfo mi)
{
// Not sure if it matters, but declaringType here is "Base".
Type declaringType = mi.DeclaringType;
BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public;
bindingAttr |= mi.IsStatic ? BindingFlags.Static : BindingFlags.Instance;
foreach (PropertyInfo info in declaringType.GetProperties(bindingAttr))
{
// For the "Id" property, info.CanRead is true, but CheckMethod is false.
if (info.CanRead && CheckMethod(mi, info.GetGetMethod(true)))
return info;
// For the "Id" property, info.CanWrite is true, but CheckMethod is false.
if (info.CanWrite && CheckMethod(mi, info.GetSetMethod(true)))
return info;
}
// This gets thrown after passing by the "Id" property that is the one I'm looking for.
throw new Exception("Method is not a property accessor");
}
private static bool CheckMethod(MethodInfo method, MethodInfo propertyMethod)
{
// These are not equal, so it goes to the next check. In the debugger, they appear identical when I look through the object tree.
if (method == propertyMethod)
return true;
Type declaringType = method.DeclaringType;
return ((declaringType.IsInterface && (method.Name == propertyMethod.Name)) && (declaringType.GetMethod(method.Name) == propertyMethod));
}
}
If anyone could help I would greatly appreciate it! I'm very much lost at this point.
Edit - If you substitute typeof(SubClass) with typeof(Base), it works, so it's something related to that relationship. I guess I could cut the issue off at the root by making an extension method like typeof(SubClass).GetPropertyFromActualClass("Id") but I'm not sure how to perform that check.
The property and method infos of Base and SubClass are indeed not equal, even if SubClass has no own implementation, as can be seen here:
var subPropertyInfo = typeof(SubClass).GetProperty("Id");
var subPropertyInfoSetter = subPropertyInfo.GetSetMethod();
var basePropertyInfo = typeof(Base).GetProperty("Id");
var basePropertyInfoSetter = basePropertyInfo.GetSetMethod();
Console.WriteLine(subPropertyInfo == basePropertyInfo); // false
Console.WriteLine(subPropertyInfoSetter == basePropertyInfoSetter); // false
If you use mi.ReflectedType instead of mi.DeclaringType, the check succeeds:
var type = subPropertyInfoSetter.ReflectedType;
Console.WriteLine(type.GetProperty("Id").GetSetMethod()
== subPropertyInfoSetter); // true