Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find only non-inherited interfaces?

I am trying to perform a query on the interfaces of a class via reflection, however the method Type.GetInterfaces() returns all the inherited interfaces also.

etc

public class Test : ITest { }

public interface ITest : ITesting { }

The code

typeof(Test).GetInterfaces();

Will return a Type[] containing both ITest and ITesting, where as I only want ITest, is there another method which allows you to specify inheritance?

Thanks, Alex.

EDIT: From the answers below I gathered this,

Type t;
t.GetInterfaces().Where(i => !t.GetInterfaces().Any(i2 => i2.GetInterfaces().Contains(i)));

The above seems to work, correct me in the comments if it does not

like image 355
Alex Hope O'Connor Avatar asked Sep 27 '11 01:09

Alex Hope O'Connor


4 Answers

You can try something like this:

Type[] allInterfaces = typeof(Test).GetInterfaces();
var exceptInheritedInterfaces = allInterfaces.Except(
  allInterfaces.SelectMany(t => t.GetInterfaces())
);

so, if you have something like this:

public interface A : B
{
}

public interface B : C
{
}

public interface C
{
}

public interface D
{
}

public class Test : A, D
{
}

The code will return A and D

like image 187
Igor Pashchuk Avatar answered Nov 15 '22 03:11

Igor Pashchuk


A bigger expert in .NET can correct me if I am wrong, but I don't believe this is possible. Internally I believe the .NET framework doesn't actually maintain that hierarchy, and it gets flattened when it is compiled to IL code.

For example, the C# code:

class Program : I2
{
    static void Main(string[] args)
    {
    }
}

interface I1
{
}

interface I2 : I1
{
}

After it is built into IL code, it is:

.class private auto ansi beforefieldinit ReflectionTest.Program
       extends [mscorlib]System.Object
       implements ReflectionTest.I2,
                  ReflectionTest.I1
{
    ...

Which is exactly the same as class Program : I1, I2


However, also in the IL is:

.class interface private abstract auto ansi ReflectionTest.I2
       implements ReflectionTest.I1
{
    ...

This means that you could write some logic to get (from my example) I1 and I2 from the Program class, then query each of those interfaces to see if they implement one of the others... In other words, since typeof(I2).GetInterfaces() contains I1, then you can infer that since typeof(Program).GetInterfaces() returns I1 and I2, then Program might not directly inherit I1 in code.

I emphasize might not because this is also valid C# code and would make the same IL code (and also the same reflection results):

class Program : I1, I2
{
    static void Main(string[] args)
    {
    }
}

interface I1
{
}

interface I2 : I1
{
}

Now Program both directly and indirectly inherits I1...

like image 35
CodingWithSpike Avatar answered Nov 15 '22 02:11

CodingWithSpike


It's not possible to simply retrieve the immediate interfaces, but we have the necessary Type metadata to figure it out.

If we have a flattened list of interfaces from the entire inheritance chain, and each interface can tell us which of its siblings it also implements/requires (which they do), we can recursively remove every interface which is implemented or required on a parent.

This approach is a little over-aggressive, in that if you declare IFoo and IBar on the immediate class AND IFoo is required by IBar, it will be removed (but really, what is this more than an exercise in curiosity? The practical usefulness of this is unclear to me...)

This code is ugly as hell, but I just threw it together in a fresh/bare MonoDevelop install...

public static void Main (string[] args)
{
    var nonInheritedInterfaces = typeof(Test).GetImmediateInterfaces();
    foreach(var iface in nonInheritedInterfaces)
    {
        Console.WriteLine(iface);
    }
    Console.Read();
}

class Test : ITest { }

interface ITest : ITestParent { }

interface ITestParent { }

public static class TypeExtensions
{
    public static Type[] GetImmediateInterfaces(this Type type)
    {
        var allInterfaces = type.GetInterfaces();
        var nonInheritedInterfaces = new HashSet<Type>(allInterfaces);
        foreach(var iface in allInterfaces)
        {
            RemoveInheritedInterfaces(iface, nonInheritedInterfaces);
        }
        return nonInheritedInterfaces.ToArray();
    }

    private static void RemoveInheritedInterfaces(Type iface, HashSet<Type> ifaces)
    {
        foreach(var inheritedIface in iface.GetInterfaces())
        {
            ifaces.Remove(inheritedIface);
            RemoveInheritedInterfaces(inheritedIface, ifaces);
        }
    }
}

private static void RemoveInheritedInterfaces(Type iface, Dictionary<Type, Type> interfaces)
{
    foreach(var inheritedInterface in iface.GetInterfaces())
    {
        interfaces.Remove(inheritedInterface);
        RemoveInheritedInterfaces(inheritedInterface, interfaces);
    }
}
like image 3
Rex M Avatar answered Nov 15 '22 03:11

Rex M


It appears there is no trivial method call to do this as basically the following are exactly the same when compiled:

MyClass : IEnumerable<bool>

and

MyClass : IEnumerable<bool>,IEnumerable

You can interrogate each interface and ask for its interfaces. And see if they are in the formers list. Unusually, for classes GetInterfaces is recursive. That is to say it gets all levels of inheritance. For interfaces it is not recursive. For instance: IList returns IList and ICollection, but makes no mention about IEnumerable which is implemented by ICollection.

However, you are most likely interested in the methods implemented uniquely by each interface and their binding in which case you can use the method GetInterfaceMap(Type).

like image 1
Michael B Avatar answered Nov 15 '22 01:11

Michael B