Reputation: 5384
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
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
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
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
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