I want to know what is fastest way to get value (only for this problem) from an object`s property ?
after some searching I saw a post from #MarkGravell in this site
He wrote this code :
using System;
using System.Reflection;
using System.Reflection.Emit;
public class Foo
{
public Foo(int bar)
{
Bar = bar;
}
private int Bar { get; set; }
}
static class Program {
static void Main()
{
var method = new DynamicMethod("cheat", typeof(int),
new[] { typeof(object) }, typeof(Foo), true);
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, typeof(Foo));
il.Emit(OpCodes.Callvirt, typeof(Foo).GetProperty("Bar",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
).GetGetMethod(true));
il.Emit(OpCodes.Ret);
var func = (Func<object, int>)method.CreateDelegate(
typeof(Func<object, int>));
var obj = new Foo(123);
Console.WriteLine(func(obj));
}
}
OR
var method = typeof(Foo).GetProperty("Bar",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.GetGetMethod(true);
var func = (Func<Foo, int>)
Delegate.CreateDelegate(typeof(Func<Foo, int>), method);
I changed it to
var pt = propertyInfo.PropertyType; // I dont know what is Type
var method = pt.GetProperty("Bar",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.GetGetMethod(true);
var func = (Func<Foo, object>) // I dont know what is return type so set object !!!
Delegate.CreateDelegate(typeof(Func<Foo, object>), method); // I want get value as object ?!!!
return func(entity).ToString(); // cast return value to string
but I got an exception
Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.
I dont know what is my property type It can be anything How customize code for this purpose ?
If anyone can help me in better way (fastest way) without property Type restriction please introduce it
The Delegate.CreateDelegate will not work in this case, because you have to cast the resulting delegate to some known type, otherwise all you have is DynamicInvoke which is not better than direct invocation of PropertyInfo (see here explanation by Marc Gravell).
The most generic way I've seen which does not involve lambda expressions (like
Sriram Sakthivel suggested) is shown by Jon Skeet here. Building on his approach and the fact we can get the actual property return type from PropertyInfo, we can invent something custom-tailored for properties invocation.
First, we define an interface:
public interface IPropertyCallAdapter<TThis>
{
object InvokeGet(TThis #this);
//add void InvokeSet(TThis #this, object value) if necessary
}
Then, an implementation of the interface:
public class PropertyCallAdapter<TThis, TResult> : IPropertyCallAdapter<TThis>
{
private readonly Func<TThis, TResult> _getterInvocation;
public PropertyCallAdapter(Func<TThis, TResult> getterInvocation)
{
_getterInvocation = getterInvocation;
}
public object InvokeGet(TThis #this)
{
return _getterInvocation.Invoke(#this);
}
}
The InvokeGet method looks mostly like the one Jon Skeet uses.
Now, to the "magic" part. We define a service which will build and cache an instance of the provider. It looks like this:
public class PropertyCallAdapterProvider<TThis>
{
private static readonly Dictionary<string, IPropertyCallAdapter<TThis>> _instances =
new Dictionary<string,IPropertyCallAdapter<TThis>>();
public static IPropertyCallAdapter<TThis> GetInstance(string forPropertyName)
{
IPropertyCallAdapter<TThis> instance;
if (!_instances.TryGetValue(forPropertyName, out instance))
{
var property = typeof(TThis).GetProperty(
forPropertyName,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
MethodInfo getMethod;
Delegate getterInvocation = null;
if (property != null && (getMethod = property.GetGetMethod(true)) != null)
{
var openGetterType = typeof(Func<,>);
var concreteGetterType = openGetterType
.MakeGenericType(typeof(TThis), property.PropertyType);
getterInvocation =
Delegate.CreateDelegate(concreteGetterType, null, getMethod);
}
else
{
//throw exception or create a default getterInvocation returning null
}
var openAdapterType = typeof(PropertyCallAdapter<,>);
var concreteAdapterType = openAdapterType
.MakeGenericType(typeof(TThis), property.PropertyType);
instance = Activator
.CreateInstance(concreteAdapterType, getterInvocation)
as IPropertyCallAdapter<TThis>;
_instances.Add(forPropertyName, instance);
}
return instance;
}
}
Here, without knowing at compile time the exact TResult type, we create the adapter and cache it for subsequent usage in order to prevent heavy reflection calls in the future.
That's it. You can use it in the following way:
PropertyCallAdapterProvider<Foo>.GetInstance("Bar").InvokeGet(fooInstance)
Also, you can easily extend this for property setters if necessary.
On my machine those are the results for accessing the getter in loop ten million times, using various methods, when the adapter instance is pre-fetched from the provider before entering the loop:
141 milliseconds for direct invocation
244 milliseconds for adapter invocation
1800 milliseconds for reflection invocation
8179 milliseconds for dynamic delegate invocation
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
};
}
Can you create a delegate of an instance method without specifying the instance at creation time? In other words, can you create a "static" delegate that takes as it's first parameter the instance the method should be called on?
For example, how can I construct the following delegate using reflection?
Func<int, string> = i=>i.ToString();
I'm aware of the fact that I can use methodInfo.Invoke, but this is slower, and does not check for type-correctness until it is called.
When you have the MethodInfo of a particular static method, it is possible to construct a delegate using Delegate.CreateDelegate(delegateType, methodInfo), and all parameters of the static method remain free.
As Jon Skeet pointed out, you can simply apply the same to make an open delegate of an instance method if the method is non-virtual on a reference type. Deciding which method to call on a virtual method is tricky, so that's no so trivial, and value-types look like they don't work at all.
For value types, CreateDelegate exhibits really weird behavior:
var func37 = (Func<CultureInfo,string>)(37.ToString);
var toStringMethod = typeof(int).GetMethod("ToString", BindingFlags.Instance | BindingFlags.Public, null, new Type[] {typeof(CultureInfo) }, null);
var func42 = (Func<CultureInfo,string>)Delegate.CreateDelegate(typeof(Func<CultureInfo,string>), 42, toStringMethod,true);
Console.WriteLine( object.ReferenceEquals(func37.Method,func42.Method)); //true
Console.WriteLine(func37.Target);//37
Console.WriteLine(func42.Target);//42
Console.WriteLine(func37(CultureInfo.InvariantCulture));//37
Console.WriteLine(func42(CultureInfo.InvariantCulture));//-201040128... WTF?
Calling CreateDelegate with null as the target object throws a binding exception if the instance method belonged to a value type (this works for reference types).
Some follow-up years later: The incorrectly-bound target that caused func42(CultureInfo.InvariantCulture); to return "-201040128" instead of "42" in my example was memory corruption that could have allowed remote code execution (cve-2010-1898); this was fixed in 2010 in the ms10-060 security update. Current frameworks correctly print 42! That doesn't make answering this question any easier, but explains the particularly weird behavior in the example.
You've actually chosen a particularly tricky example, for two reasons:
ToString() is a virtual method inherited from object but overridden in Int32.
int is a value type, and there are weird rules with Delegate.CreateDelegate() when it comes to value types and instance methods - basically the first effective parameter becomes ref int rather than int
However, here's an example for String.ToUpper, which doesn't have either of those problems:
using System;
using System.Reflection;
class Test
{
static void Main()
{
MethodInfo method = typeof(string).GetMethod
("ToUpper", BindingFlags.Instance | BindingFlags.Public,
null, new Type[]{}, null);
Func<string, string> func = (Func<string, string>)
Delegate.CreateDelegate(typeof(Func<string, string>),
null,
method);
string x = func("hello");
Console.WriteLine(x);
}
}
If that's good enough for you, great... if you really want int.ToString, I'll have to try a bit harder :)
Here's an example for a value type, using a new delegate type which takes its first parameter by reference:
using System;
using System.Reflection;
public struct Foo
{
readonly string value;
public Foo(string value)
{
this.value = value;
}
public string DemoMethod()
{
return value;
}
}
class Test
{
delegate TResult RefFunc<TArg, TResult>(ref TArg arg);
static void Main()
{
MethodInfo method = typeof(Foo).GetMethod
("DemoMethod", BindingFlags.Instance | BindingFlags.Public,
null, new Type[]{}, null);
RefFunc<Foo, string> func = (RefFunc<Foo, string>)
Delegate.CreateDelegate(typeof(RefFunc<Foo, string>),
null,
method);
Foo y = new Foo("hello");
string x = func(ref y);
Console.WriteLine(x);
}
}
I'm not sure, but may be Open delegates can help you.
Upd: Follow this link, if first one don't works.
You could use Lambdas to get a "somewhat" compiled static wrapper for your instance method.
The sample below isn't exactly blazingly fast, yet it should be significantly faster than any plain dynamic invoke.
The output
100000 iterations took 4 ms
1000000 iterations took 18 ms
10000000 iterations took 184 ms
The code
class Program
{
public sealed class Test
{
public String Data { get; set; }
public override string ToString()
{
return Data;
}
}
static void Main(string[] args)
{
TestRun(100000);
TestRun(1000000);
TestRun(10000000);
}
private static void TestRun(int iterations)
{
var toString = typeof(Test).GetMethod("ToString",
BindingFlags.Instance
| BindingFlags.Public,
null,
Type.EmptyTypes,
null);
var call = GetCall<Test, String>(toString);
var tests
= (from i in Enumerable.Range(1, iterations)
select new Test { Data = "..." + i }).ToList();
var sw = Stopwatch.StartNew();
tests.ForEach(i => call(i));
sw.Stop();
Console.WriteLine("{0} iterations took {1} ms", iterations, sw.ElapsedMilliseconds);
}
private static Func<T, M> GetCall<T, M>(MethodInfo methodInfo)
{
var input = Expression.Parameter(typeof(T), "input");
MethodCallExpression member = Expression.Call(input, methodInfo);
var lambda = Expression.Lambda<Func<T, M>>(member, input);
return lambda.Compile();
}
}
The goog way maybe can be useing the "dynamic" type in .NET 4.0. However the Delegate need the instance (for non-static methods). The problems is more complex then lokks at first time because of polymorfism etc...
public abstract class BaseClass
{
protected virtual int getValue() { return 1; }
}
I want to call getValue method.
typeof(BaseClass)
.GetMethod("getValue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
.Invoke(null, new object[] { });
This code has a error.
Non static method requires a target
However, I cannot create instance of BaseClass because it is abstract class.
So, I am using dummy class which extends BaseClass.
class DummyClass : BaseClass { }
typeof(BaseClass)
.GetMethod("getValue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
.Invoke(new DummyClass(), new object[] { });
I don't want to create dummy class every time if possible.
Is there a better way?
I agree with the other comments that this should not be production code, but out of fun it can certainly be done:
public static T CreateAbstractInstance<T>() where T : class =>
(T)Activator.CreateInstance(
Thread.GetDomain()
.DefineDynamicAssembly(new AssemblyName("DynamicAssembly"), AssemblyBuilderAccess.Run)
.DefineDynamicModule("DynamicModule")
.DefineType("DynamicType", TypeAttributes.Public | TypeAttributes.Class, typeof(T))
.CreateType());
private static TResult AbstractInvoke<TClass, TResult>(string methodName) where TClass : class
{
var method = typeof(TClass).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
var elegate = method.CreateDelegate(typeof(Func<BaseClass, TResult>));
return (TResult)elegate.DynamicInvoke(CreateAbstractInstance<TClass>());
}
private static void Main()
{
var result = AbstractInvoke<BaseClass, int>("getValue");
Console.WriteLine(result);
}
There is probably some method to trick the compiler and invoke it without even creating an instance, but I was getting too many Visual Studio crashes to carry on investigating other methods. Expressions seems to lead nowhere as at the end of the day, it still had to generate valid code so no way to trick an expression into an invalid lambda.
If you reuse a singleton instance from CreateAbstractInstance then you shouldn't have overheads after the first object is created (beside reflection usage if you invoke non public methods).
I thought I might add this to the answer as well:
I notice the comment about using it in a unit test; if you are using Moq (and probably most of the decent frameworks), you can do this:
var bc = new Mock<BaseClass>().Object;
// Invoke whatever you want on `bc` with reflection or not.
Although be aware that if the method is virtual it will automatically override it so you won't get by default the base class logic, but it can be solved setting up the mock.
You can try using an open instance delegate. Try this:
var method = typeof(BaseClass).GetMethod("getValue");
var func = (Func<BaseClass, int>) Delegate.CreateDelegate(typeof(Func<BaseClass, int>), method);
int result = func(null);
Note: The above technique works by omitting the hidden this parameter in the member call. This will only work if your method does not need to access the instance. If you attempt to access the instance, you will get a null reference exception.
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();
}
}
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