Получение листовых интерфейсов типа - PullRequest
3 голосов
/ 22 сентября 2009

Класс System.Type предоставляет метод GetInterfaces (), который «получает все интерфейсы, реализованные или унаследованные текущим типом». Проблема заключается в том, что «метод GetInterfaces не возвращает интерфейсы в определенном порядке, например в алфавитном порядке или в порядке объявления. Ваш код не должен зависеть от порядка, в котором возвращаются интерфейсы, поскольку этот порядок меняется». В моем случае, однако, мне нужно изолировать и представить (через WCF) только конечные интерфейсы иерархии интерфейсов, то есть интерфейсы, которые не наследуются другими интерфейсами в этой иерархии. Например, рассмотрим следующую иерархию

interface IA { }
interface IB : IA { }
interface IC : IB { }
interface ID : IB { }
interface IE : IA { }
class Foo : IC, IE {}

Листовыми интерфейсами Foo являются IC и IE, тогда как GetInterfaces () вернет все 5 интерфейсов (IA..IE). Также предусмотрен метод FindInterfaces (), позволяющий фильтровать вышеупомянутые интерфейсы с использованием предиката по вашему выбору.

Моя текущая реализация приведена ниже. Это O (n ^ 2), где n - количество интерфейсов, которые реализует тип. Мне было интересно, есть ли более изящный и / или эффективный способ сделать это.

    private Type[] GetLeafInterfaces(Type type)
    {
        return type.FindInterfaces((candidateIfc, allIfcs) =>
        {
            foreach (Type ifc in (Type[])allIfcs)
            {
                if (candidateIfc != ifc && candidateIfc.IsAssignableFrom(ifc))
                    return false;    
            }
            return true;
        }
        ,type.GetInterfaces());
    }

Заранее спасибо

1 Ответ

3 голосов
/ 22 сентября 2009

Я не думаю, что вы найдете простое решение для этого. Ваша проблема в том, что в итоге Foo фактически реализует все интерфейсы , независимо от их внутренней иерархии наследования. Если вы изучите Foo с помощью Ildasm или подобного инструмента, это станет очевидным. Рассмотрим следующий код:

interface IFirst { }
interface ISecond : IFirst { }
class Concrete : ISecond { }

Полученный IL-код (сбрасывается с Ildasm):

.class private auto ansi beforefieldinit MyNamespace.Concrete
       extends [mscorlib]System.Object
       implements MyNamespace.ISecond,
                  MyNamespace.IFirst
{
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Concrete::.ctor

} // end of class MyNamespace.Concrete

Как видите, на этом уровне нет разницы в отношении между Concrete и двумя интерфейсами.

...