-2

Given the following code:

var dict = new Dictionary<string,string>() {
    {"",""}
};
Expression<Func<string>> expr = () => dict[""];

expr.Body returns an instance of MethodCallExpression, whose Method property returns the get__Item MethodInfo.

There doesn't seem to be any information which I can use to detect that the method being called (get__Item) is the method underlying an indexer.

How can I detect that a given MethodInfo refers to the method underlying an indexer?

This is not a duplicate of Indentifying a custom indexer using reflection in C#, because (as noted in the title, the comments, and this answer) I don't have a PropertyInfo, only a MethodInfo; and the linked question is asking about identifying a specific PropertyInfo as the indexer.

I am trying to map expression trees to Roslyn SyntaxNodes, and the above expression tree should not be mapped as:

() => dict.Item("")

or:

() => dict.get__Item("")

but rather, as the original source code:

() => dict[""]
Zev Spitz
  • 13,950
  • 6
  • 64
  • 136

1 Answers1

0

You can determine which property (if any) is an indexer by inspecting the type. (You're looking at a method, not a property, but I'll get to that.)

From the DefaultMemberAttribute reference

The C# compiler emits the DefaultMemberAttribute on any type containing an indexer.

So the question becomes

  • does the type on which the method is invoked have that attribute?
  • is the method you're inspecting either the getter or setter for that property?

If the answer to both is "yes" then the method accesses an indexer property.

Here are a few functions. This isn't pretty. I'm not questioning whether or not your reason makes sense. I just found it interesting.

public static class ReflectionExtensions
{
    public static bool IsIndexerPropertyMethod(this MethodInfo method)
    {
        var declaringType = method.DeclaringType;
        if (declaringType is null) return false;
        var indexerProperty = GetIndexerProperty(method.DeclaringType);
        if (indexerProperty is null) return false;
        return method == indexerProperty.GetMethod || method == indexerProperty.SetMethod;
    }

    private static PropertyInfo GetIndexerProperty(this Type type)
    {
        var defaultPropertyAttribute = type.GetCustomAttributes<DefaultMemberAttribute>()
            .FirstOrDefault();
        if (defaultPropertyAttribute is null) return null;
        return type.GetProperty(defaultPropertyAttribute.MemberName, 
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    }
}

These are not the most glorious unit tests, but I like to include them anyway.

[TestClass]
public class ReflectionExtensionTests
{
    [TestMethod]
    public void DetectsIndexer()
    {
        var dict = new Dictionary<string, string>() {
            {"",""}
        };
        Expression<Func<string>> expr = () => dict[""];
        var method = (expr.Body as MethodCallExpression).Method;
        Assert.IsTrue(method.IsIndexerPropertyMethod());
    }

    [TestMethod]
    public void DetectsNotIndexer()
    {
        var dict = new Dictionary<string, string>() {
            {"",""}
        };
        Expression<Action<string, string>> expr = (s, s1) => dict.Add(s, s1);
        var method = (expr.Body as MethodCallExpression).Method;
        Assert.IsFalse(method.IsIndexerPropertyMethod());
    }

    [TestMethod]
    public void DetectsRenamedIndexer()
    {
        var myClass = new ClassWithRenamedIndexer();
        Expression<Func<int>> expr = () => myClass[2];
        var method = (expr.Body as MethodCallExpression).Method;
        Assert.IsTrue(method.IsIndexerPropertyMethod());
    }

    class ClassWithRenamedIndexer
    {
        [IndexerName("blarg")]
        public int this[int index]    // Indexer declaration  
        {
            get { return 1; }
        }
    }
}
Scott Hannen
  • 27,588
  • 3
  • 45
  • 62