Kusek
Kusek

Reputation: 5384

AmbiguousMatchException - Type.GetProperty - C# Reflection

Yesterday I ran into an Issue while developing a Web Part (This question is not about webpart but about C#). Little background about the Issue. I have a code that load the WebPart using the Reflection, In which I got the AmbiguousMatchException. To reproduce it try the below code

        public class TypeA
        {
            public virtual int Height { get; set; }
        }
        public class TypeB : TypeA
        {
            public String Height { get; set; }
        }
        public class Class1 : TypeB
        {

        }

        Assembly oAssemblyCurrent = Assembly.GetExecutingAssembly();
        Type oType2 = oAssemblyCurrent.GetType("AmbigousMatchReflection.Class1");
        PropertyInfo oPropertyInfo2 = oType2.GetProperty("Height");//Throws AmbiguousMatchException 
        oPropertyInfo2 = oType2.GetProperty("Height", 
            BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);  // I tried this code Neither these BindingFlags or any other didnt help

I wanted to know the BindingFlag to Fetch the Height Property. You will have the question of why I wanted to create another Height Property that is already there in the Base class. That is how the Microsoft.SharePoint.WebPartPages.PageViewerWebPart was designed check the Height property of the PageViewerWebPart class.

Upvotes: 7

Views: 8490

Answers (4)

Lazlo
Lazlo

Reputation: 8790

I created two extension methods extending on Jon Skeet's answer. You can place those in any public static class.

Edit: Removed the MissingMemberException to behave more like the default .NET implementations which return null on failure.

Usage:

var field = type.GetFieldUnambiguous(type, "FieldName", bindingFlags);
var property = type.GetPropertyUnambiguous(type, "PropertyName", bindingFlags);

Implementation:

public static FieldInfo GetFieldUnambiguous(this Type type, string name, BindingFlags flags)
{
    if (type == null) throw new ArgumentNullException(nameof(type));
    if (name == null) throw new ArgumentNullException(nameof(name));

    flags |= BindingFlags.DeclaredOnly;

    while (type != null)
    {
        var field = type.GetField(name, flags);

        if (field != null)
        {
            return field;
        }

        type = type.BaseType;
    }

    return null;
}

public static PropertyInfo GetPropertyUnambiguous(this Type type, string name, BindingFlags flags
{
    if (type == null) throw new ArgumentNullException(nameof(type));
    if (name == null) throw new ArgumentNullException(nameof(name));

    flags |= BindingFlags.DeclaredOnly;

    while (type != null)
    {
        var property = type.GetProperty(name, flags);

        if (property != null)
        {
            return property;
        }

        type = type.BaseType;
    }

    return null;
}

Upvotes: 2

Sam Saffron
Sam Saffron

Reputation: 131202

See the following example:

    class Foo {
        public float Height { get; set; }
    }

    class Bar : Foo {
        public int Height { get; set; }
    }

    class BarBar : Bar { }

    class Foo2 : Foo{
        public float Height { get; set; }
    }

    class BarBar2 : Foo2 { } 

    static void Main(string[] args) {

        // works 
        var p = typeof(BarBar).GetProperty("Height", typeof(float), Type.EmptyTypes);

        // works
        var p2 = typeof(BarBar).BaseType.GetProperty("Height", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);

        // works
        var p3 = typeof(BarBar2).GetProperty("Height");

        // fails
        var p4 = typeof(BarBar).GetProperty("Height"); 

        Console.WriteLine(p);

    }
  • You get an AmbiguousMatchException if a two or more properties with the differing return types and the same name live in your inheritance chain.

  • Stuff resolves just fine if you override an implementation (using new or override) and maintain the return type.

  • You can force reflection only to look at the properties for a particular type.

Upvotes: 4

Jon Skeet
Jon Skeet

Reputation: 1504132

There are two Height properties there, and neither of them are declared by Class1 which you're calling GetProperty on.

Now, would it be fair to say you're looking for "the Height property declared as far down the type hiearchy as possible"? If so, here's some code to find it:

using System;
using System.Diagnostics;
using System.Reflection;

public class TypeA
{
    public virtual int Height { get; set; }
}

public class TypeB : TypeA
{
    public new String Height { get; set; }
}

public class Class1 : TypeB
{        
}

class Test
{
    static void Main()
    {
        Type type = typeof(Class1);
        Console.WriteLine(GetLowestProperty(type, "Height").DeclaringType);
    }

    static PropertyInfo GetLowestProperty(Type type, string name)
    {
        while (type != null)
        {
            var property = type.GetProperty(name, BindingFlags.DeclaredOnly | 
                                                  BindingFlags.Public |
                                                  BindingFlags.Instance);
            if (property != null)
            {
                return property;
            }
            type = type.BaseType;
        }
        return null;
    }
}

Note that if you know the return types will be different, it may be worth simplifying the code as shown in sambo99's answer. That would make it quite brittle though - changing the return type later could then cause bugs which would only be found at execution time. Ouch. I'd say that by the time you've done this you're in a brittle situation anyway :)

Upvotes: 16

Tetraneutron
Tetraneutron

Reputation: 33861

Obviously there are two properties that match the name you have given of "Height", one with return type int, and another string., Just add the return type as the second parameter tot the GetPropertyCall depending on which you want returning and this ambiguity should disappear.

Upvotes: 2

Related Questions