Reputation: 581
I am trying to get a MethodInfo object and call Invoke() on it. Here's what I've tried so far:
public class ContactCustomFieldsFacet
{
public Dictionary<string, string> Fields { get; set; } = new Dictionary<string, string>();
}
public static class FacetExtensions
{
public static Func<ContactCustomFieldsFacet, object> Compile(string body)
{
ParameterExpression prm = Expression.Parameter(typeof(ContactCustomFieldsFacet), typeof(ContactCustomFieldsFacet).Name);
LambdaExpression exp = DynamicExpressionParser.ParseLambda(new[] { prm }, typeof(object), body);
return (Func<ContactCustomFieldsFacet, object>)exp.Compile();
}
}
var lambda = FacetExtensions.Compile($"ContactCustomFieldsFacet.Fields[\"test\"]");
var propertyMethod = lambda.Method;
// No control over the below code - it's called by a third party library
ContactCustomFieldsFacet facet = GetFacet();
propertyMethod.Invoke(null, new [] { facet }); // System.ArgumentException: MethodInfo must be a runtime MethodInfo object.
Essentially I want to be able to dynamically create an expression that has the ContactCustomFieldsFacet.Fields
dictionary index field hardcoded for that instance.
I realise I might be on the wrong track so any pointers would be appreciated.
.NET Fiddle: https://dotnetfiddle.net/ecMRye
EDIT
Thanks to Corey I've updated the code but am still getting a similar issue
.NET Fiddle: https://dotnetfiddle.net/5hDOsd
Upvotes: 1
Views: 998
Reputation: 16574
Locating the method you want to call is reasonably simple reflection. The hard part is figuring out the name of the method to call. Sometimes the simplest thing to do is to create an expression and have a look at how the Compiler put it together.
For instance, this:
Expression<Func<Dictionary<string, string>, string>> expr = d => d["test"];
If you inspect (or Dump()
, if you're using LINQPad) the expr
variable you'll be able to wander through the generated expression tree and look at things like the method information in the Call
node.
In your case it seems that you want to invoke get_Item
on the Dictionary<string, string>
class. It requires an object reference and a string parameter for the key. Object instance will be the Fields
property value from the supplied ContactCustomFieldsFacet
so we'll need to grab that.
Building the whole thing by hand might look something like this:
public static Func<ContactCustomFieldsFacet, string> MakeAccessor(string key)
{
var tFacet = typeof(ContactCustomFieldsFacet);
var piFields = tFacet.GetProperty("Fields");
var miFieldsGet = piFields.PropertyType.GetMethod("get_Item");
var param = Expression.Parameter(tFacet, "facet");
var lambda = Expression.Lambda<Func<ContactCustomFieldsFacet, object>>
(
Expression.Call
(
// Instance for invocation: param.Fields
Expression.MakeMemberAccess(param, piFields),
// Method to call
miFieldsGet,
// Call arguments
Expresion.Constant(key)
),
param
);
return lambda.Compile();
}
Invoking that method will get you a Func<>
that returns the value for a specific key
, as set at the time the Func<>
was built. For instance:
var fnGetTest = MakeAccessor("test");
var value = fnGetTest(someFacetInstance);
This all assumes that the dictionary stores reference types - string works fine. If it's a value type (int for example) you'll need to box the value by Convert
ing to object
.
Upvotes: 2