relatively_random
relatively_random

Reputation: 5126

Properly finding an inherited interface property through reflection

This is a two-part question. First, which of these explicit property implementations gets bound to IAllTogether.SomeInt and why doesn't the compiler complain about ambiguity? It does when you comment-out the marked lines.

public interface IFirst
{
    int SomeInt { get; }
}

public interface ISecond
{
    int SomeInt { get; }
}

public interface ICombined : IFirst, ISecond
{
    new int SomeInt { get; } // Comment this line.
}

public interface IAllTogether : IFirst, ISecond, ICombined
{ }

public sealed class Implementation : IAllTogether
{
    int ICombined.SomeInt { get { return 0; } } // Comment this line.

    int IFirst.SomeInt { get { return 0; } }

    int ISecond.SomeInt { get { return 0; } }
}


IAllTogether t = new Implementation();
var unimportant = t.SomeInt;

Second question would be: how do I find the right PropertyInfo when given an interface Type and a name of the property? I can use GetInterfaces() and GetProperty() to list all the possible candidates, but how do I know which is the right one? I tried typeof(IAllTogether).GetProperty("SomeInt"), but it doesn't work.

Edit

Looks like the answer to the first part is that the hiding of inherited members resolves ambiguity. However, not even a single comment yet on the second part: how to reliably find the proper PropertyInfo for some property name and an interface type.

Edit 2

Clarification on the second part of the question. What I'm looking for is a way to get the right property for any unknown Type. Basically, a method like this:

public static PropertyInfo GetPropertyOfInterface(Type interfaceType, string propertyName)
{
    if (!interfaceType.IsInterface)
        throw new ArgumentException();

    // for interfaceType == typeof(IAllTogether), return ICombined.SomeInt
    // for interfaceType == typeof(IFirst), return IFirst.SomeInt
}

Upvotes: 0

Views: 179

Answers (2)

relatively_random
relatively_random

Reputation: 5126

Lots of people answered the first part: if an interface hides members of the original interface, those are not considered.

Using that information, here's my attempt at the second part. Comments on issues or improvements are welcome.

public static PropertyInfo GetPropertyOfInterface(Type interfaceType, string propertyName)
{
    if (interfaceType == null)
        throw new ArgumentNullException("interfaceType");

    if (!interfaceType.IsInterface)
        throw new ArgumentException(
            string.Format("Type {0} doesn't represent an interface.",
                interfaceType.FullName),
            "interfaceType");

    // If declared in given interface, just return that.
    var declaredProperty = interfaceType.GetProperty(propertyName);
    if (declaredProperty != null)
        return declaredProperty;

    // Otherwise, first finding all the candidates.
    var candidates = new HashSet<PropertyInfo>(
        interfaceType.GetInterfaces().Select(t => t.GetProperty(propertyName)));
    candidates.Remove(null);

    if (candidates.Count == 0)
        throw new ArgumentException(
            string.Format("Property {0} not found in interface {1}.",
                propertyName, interfaceType.FullName),
            "propertyName");

    // Finally, removing all candidates the first candidates hide.
    var originalCandidates = candidates.ToList();
    candidates.ExceptWith(
        originalCandidates.SelectMany(prop => prop.DeclaringType.GetInterfaces())
                          .Select(t => t.GetProperty(propertyName)));

    if (candidates.Count != 1)
        throw new AmbiguousMatchException(
            string.Format("Property {0} is ambiguous in interface {1}.",
                propertyName, interfaceType.FullName));

    return candidates.First();
}

Upvotes: 1

Chetan
Chetan

Reputation: 6891

To answer your first question.

Compiler only need to know is that the interface members are implemented or not. It is the runtime who decides which version of the member to invoke.

In your example you are using explicit implementation.

In this case compile first checks if SomeInt property is implemented or not. If that is implicitly implemented it won't complain. If any one of the interface property is implemented explicitly compiler will check if rest o the interface properties are implemented either by implicit or explicit.

Upvotes: 0

Related Questions