13

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

H.B.
  • 166,899
  • 29
  • 327
  • 400
Alex Hope O'Connor
  • 9,354
  • 22
  • 69
  • 112
  • 3
    What if it's `public class Test : ITest, ITesting`? Why do you want this? – SLaks Sep 27 '11 at 01:35
  • 3
    Because I would like to know what interfaces the class is inheriting from directly? and not the interfaces it is inheriting from indirectly, to dynamically generate new types. Does it matter why? – Alex Hope O'Connor Sep 27 '11 at 01:48
  • 5
    Strictly speaking, you don't *inherit* interfaces, you *implement* them, so class `Test` is providing an implementation of `ITest` and `ITesting` in this case. The fact that `ITest` "inherits" from `ITesting` simply means that any type implementing `ITest` has to provide an implementation of `ITesting` too. – LukeH Sep 27 '11 at 01:50
  • 3
    @Alex HopeO'Connor: Yes, it matters why. Often people show up trying to solve problem X and think the only way to solve it is Y so they ask about Y when it turns out if they had cast a bigger net they might have received solutions to their problem X. Knowing the why helps us point you towards the right solution. – jason Sep 27 '11 at 01:58
  • your latest update seems to work but it's somewhat inefficient -- see my answer below for a slightly different approach – Igor Pashchuk Sep 27 '11 at 02:42

7 Answers7

10

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

Igor Pashchuk
  • 2,455
  • 2
  • 22
  • 29
4

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...

CodingWithSpike
  • 42,906
  • 18
  • 101
  • 138
3

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);
    }
}
Rex M
  • 142,167
  • 33
  • 283
  • 313
1

In my previous attempt I could not succeed.

 Type[] types = typeof(Test).GetInterfaces();

 var directTypes = types.Except
                    (types.SelectMany(t => t.GetInterfaces()));

  foreach (var t in directTypes)
  {

  }

Hope this will helps someone.

KV Prajapati
  • 93,659
  • 19
  • 148
  • 186
  • 1
    this *looks* like it should work perfectly. alternative non-LINQ syntax: `typeof(Test).GetInterfaces().Where(i => i.GetInterfaces().Length >= 1).SingleOrDefault();` – Igor Pashchuk Sep 27 '11 at 02:01
  • 1
    Would this not fall over if the class directly implemented two interfaces. `Test : ITestInterface1, ITestInterface2` ? (by fall over I mean neglect to show the class directly implements more than 1 interface) – Phill Sep 27 '11 at 02:08
  • I agreed prematurely... if interface A inherits from interface B and B inherits from interface C; and my type implements A, I don't want the query to return A and B -- only A. Of course, SingleOrDefault() will ensure I only get one of them but it's not deterministic. The other problem is when a type implements multiple non-related interfaces. – Igor Pashchuk Sep 27 '11 at 02:11
  • 1
    @Igor, your suggestion and the original code are both Linq. Linq is not one syntax versus another, it's the library and, more than that, the *idea*. Secondly, `SingleOrDefault` in no way *only gets one of them.* SingleOrDefault *expects* 0 or 1, and if it is anything other than that, *it throws an exception.* `FirstOrDefault` will get the first of arbitrarily many candidates without error. Lastly, I don't know what you mean by *not deterministic.* Ignoring `Single` vs. `First`, it will simply get the first element. The only way that is non-deterministic is if the sequence itself is random. – Anthony Pegram Sep 27 '11 at 15:28
  • 1
    @AnthonyPegram, my understanding is that LINQ, as the term implies, is "language-integrated query". It certainly is possible because of the LINQ library of extension methods. But, in my mind, LINQ is mainly the new syntactical sugar (`from...where...select`). I'm not sure what the proper term would be for what I was trying to say -- perhaps "fluent API" or "method chaining". You're right about `SingleOrDefault` -- I did confuse it with `FirstOrDefault`. The determinism refers to the first interface returned by the query (according to MSDN, `GetInterfaces()` is not deterministic). – Igor Pashchuk Sep 28 '11 at 00:52
1

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).

Michael B
  • 7,512
  • 3
  • 31
  • 57
0

try this:

    Type t = typeof(Test);
    int max = 0;
    Type result = null;
    foreach (Type type in t.GetInterfaces())
    {
        int len = type.GetInterfaces().Length;
        if (len > max)
        {
            max = len;
            result = type;
        }
    }
    if (result != null)
        Console.WriteLine(result.FullName);
ojlovecd
  • 4,812
  • 1
  • 20
  • 22
0
    Type t = typeof(Test);
    int max = 0;
    List<Type> results = new List<Type>();
    foreach (Type type in t.GetInterfaces())
    {
        int len = type.GetInterfaces().Length;
        if (len > max)
        {
            max = len;
            results.Add(type);
        }
    }
    if (results.Count > 0)
        foreach (Type type in results)
            Console.WriteLine(type.FullName);
ojlovecd
  • 4,812
  • 1
  • 20
  • 22