Найти только не унаследованные интерфейсы? - PullRequest
11 голосов
/ 27 сентября 2011

Я пытаюсь выполнить запрос к интерфейсам класса с помощью отражения, однако метод Type.GetInterfaces () также возвращает все унаследованные интерфейсы.

и т.д.

public class Test : ITest { }

public interface ITest : ITesting { }

код

typeof(Test).GetInterfaces();

Вернет Type[], содержащий как ITest, так и ITesting, где, как я хочу только ITest, есть ли другой метод, который позволяет указывать наследование?

Спасибо, Алекс.

EDIT: Из приведенных ниже ответов я собрал это,

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

Выше, кажется, работает, поправьте меня в комментариях, если это не так

Ответы [ 7 ]

8 голосов
/ 27 сентября 2011

Вы можете попробовать что-то вроде этого:

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

Итак, если у вас есть что-то вроде этого:

public interface A : B
{
}

public interface B : C
{
}

public interface C
{
}

public interface D
{
}

public class MyType : A, D
{
}

Код вернет A и D

4 голосов
/ 27 сентября 2011

Большой специалист по .NET может исправить меня, если я ошибаюсь, но я не верю, что это возможно.Внутренне я считаю, что .NET Framework на самом деле не поддерживает эту иерархию, и она сглаживается, когда она компилируется в код IL.

Например, код C #:

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

interface I1
{
}

interface I2 : I1
{
}

Послеон встроен в код IL, это:

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

Что в точности совпадает с class Program : I1, I2


Однако, также в IL есть:

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

Это означает, что вы могли бы написать некоторую логику для получения (из моего примера) I1 и I2 из класса Program, а затем запросить каждый из этих интерфейсов, чтобы выяснить, реализуют ли они один из других ..Другими словами, поскольку typeof(I2).GetInterfaces() содержит I1, вы можете сделать вывод, что, поскольку typeof(Program).GetInterfaces() возвращает I1 и I2, то Program может напрямую не наследовать I1 вcode.

Подчеркну, что может и не , потому что это также допустимый код C # и будет иметь тот же код IL (а также те же результаты отражения):

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

interface I1
{
}

interface I2 : I1
{
}

Теперь Program прямо и косвенно наследует I1 ...

2 голосов
/ 27 сентября 2011

Невозможно просто получить непосредственные интерфейсы, но у нас есть необходимые метаданные типа, чтобы понять это.

Если у нас есть плоский список интерфейсов из всей цепочки наследования, и каждый интерфейс может сказать нам, какой из его родных элементов он также реализует / требует (что они делают), мы можем рекурсивно удалить каждый интерфейс, который реализован или требуется для родитель.

Этот подход немного чрезмерно агрессивен, в том случае, если вы объявляете IFoo и IBar в непосредственном классе AND, а IFoo требуется IBar, он будет удален (но на самом деле, что это больше, чем упражнение в любопытстве? Практическая польза от этого мне неясна ...)

Этот код ужасен до чертиков, но я просто скомбинировал его в новой / простой установке MonoDevelop ...

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);
    }
}
1 голос
/ 27 сентября 2011

Похоже, что нет никакого тривиального вызова метода, чтобы сделать это, так как в основном следующее точно то же самое при компиляции:

MyClass : IEnumerable<bool>

и

MyClass : IEnumerable<bool>,IEnumerable

Вы можете опросить каждый интерфейс испросите его интерфейсы.И посмотрите, есть ли они в списке бывших.Необычно, что для классов GetInterfaces является рекурсивным.То есть он получает все уровни наследования.Для интерфейсов это не рекурсивно.Например: IList возвращает IList и ICollection, но не упоминает о IEnumerable, который реализован ICollection.

Однако вас, скорее всего, интересуют методы, реализуемые уникальным образом каждым интерфейсом, и их привязка, и в этом случае вы можете использовать метод GetInterfaceMap(Type).

1 голос
/ 27 сентября 2011

В моей предыдущей попытке я не смог добиться успеха.

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

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

  foreach (var t in directTypes)
  {

  }

Надеюсь, это кому-нибудь поможет.

0 голосов
/ 27 сентября 2011
    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);
0 голосов
/ 27 сентября 2011

попробуйте это:

    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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...