Find only non-inherited interfaces?
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
...
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)
.
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);
}
}
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