Как я перечисляю список интерфейсов, которые непосредственно определяются на наследующем классе?

Обновленный вопрос, данный корректный ответ Andrew Hare:

Учитывая следующие классы C#:

public class Bar : Foo, IDisposable
{
    // implementation of Bar and IDisposable
}

public class Foo : IEnumerable<int>
{
    // implementation of Foo and all its inherited interfaces
}

Я хочу метод как следующий, который не перестал работать на утверждениях (Примечание: Вы не можете изменить утверждения):

public void SomeMethod()
{
   // This doesn't work
   Type[] interfaces = typeof(Bar).GetInterfaces();

   Debug.Assert(interfaces != null);
   Debug.Assert(interfaces.Length == 1);
   Debug.Assert(interfaces[0] == typeof(IDisposable));
}

Кто-то может помочь путем фиксации этого метода, таким образом, утверждения не перестали работать?

Вызов typeof(Bar).GetInterfaces() не работает, потому что это возвращает всю интерфейсную иерархию (т.е. interfaces переменная содержит IEnumerable<int>, IEnumerable, и IDisposable), не только верхний уровень.

8
задан Jordan 25 March 2010 в 19:50
поделиться

6 ответов

Попробуйте следующее:

using System.Linq;    
public static class Extensions
{
    public static Type[] GetTopLevelInterfaces(this Type t)
    {
        Type[] allInterfaces = t.GetInterfaces();
        var selection = allInterfaces
            .Where(x => !allInterfaces.Any(y => y.GetInterfaces().Contains(x)))
            .Except(t.BaseType.GetInterfaces());
        return selection.ToArray();
    }
}

использование:

    private void Check(Type t, Type i)
    {
        var interfaces = t.GetTopLevelInterfaces();

        Debug.Assert(interfaces != null, "interfaces is null");
        Debug.Assert(interfaces.Length == 1, "length is not 1");
        Debug.Assert(interfaces[0] == i, "the expected interface was not found");

        System.Console.WriteLine("\n{0}", t.ToString());
        foreach (var intf in  interfaces)
            System.Console.WriteLine("  " + intf.ToString());

    }

    public void Run()
    {
        Check(typeof(Foo), typeof(IEnumerable<int>));
        Check(typeof(Bar), typeof(IDisposable));
    }

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

8
ответ дан 5 December 2019 в 12:09
поделиться

Примечание. Обновлено также для фильтрации унаследованных интерфейсов.

Вы можете исключить элементы базового интерфейса, например:

public Type[] GetDeclaredInterfaces( Type type )
{
    if( type == typeof(object) )
        return new Type[ 0 ];    
    Type[] interfaces = type.GetInterfaces();
    Type[] baseInterfaces = interfaces.Where( i => i.BaseType != null && i.BaseType.IsInterface );
    Type[] declaredInterfaces = interfaces.Except( type.BaseType.GetInterfaces() );
    return declaredInterfaces.Except( baseInterfaces );
}
0
ответ дан 5 December 2019 в 12:09
поделиться

Эндрю Хэйр прав в том, что вы не можете получить указанный список интерфейсов с помощью отражения. Однако вы можете найти интерфейсы «верхнего уровня», исключив любые интерфейсы, подразумеваемые другими. Вы можете реализовать это так:

Type[] allInterfaces = typeof(Foo).GetInterfaces();
Type[] interfaces = allInterfaces
   .Where(x => !allInterfaces.Any(y => y.GetInterfaces().Contains(x)))
   .ToArray();

Это передает ваши утверждения.

2
ответ дан 5 December 2019 в 12:09
поделиться

Вы просто хотите получить интерфейсы первого уровня, верно? Вы можете смешать LINQ и рефлексию; просто исключите все, что реализует базовый тип.

var fooType = typeof(Foo);

if(fooType.BaseType == null)
  return fooType.GetInterfaces().ToArray();

return fooType 
  .GetInterfaces()
  .Except(fooType.BaseType.GetInterfaces())
  .ToArray();
2
ответ дан 5 December 2019 в 12:09
поделиться

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

Другими словами, если вы посмотрите на IL для созданного вами класса, вы увидите следующее:

.class public auto ansi beforefieldinit Foo
        extends [mscorlib]System.Object
        implements [mscorlib]System.Collections.Generic.IEnumerable`1<int32>, 
                   [mscorlib]System.Collections.IEnumerable
{
    // ... 
}

Несмотря на то, что вы только указали, что ваш тип реализует IEnumerable , компилятор имеет испущенный IL, который указывает, что ваш тип реализует IEnumerable и IEnumerable .

API отражения с радостью возвращает то, что вы фактически определили для типа (а именно, что ваш тип реализует оба интерфейса - что он и делает). Компилятор C # позволяет вам ссылаться только на самый нижний тип в иерархии интерфейсов, поскольку он будет заполнять другие интерфейсы, которые также реализует ваш тип. Это один из отличий наследования интерфейсов от наследования типов.

2
ответ дан 5 December 2019 в 12:09
поделиться

Я бы написал это так:

public void SomeMethod()
{
   Type[] interfaces = typeof(Foo).GetInterfaces();
   Debug.Assert(interfaces.Contains(typeof(IEnumerable<int>)));
}

Но трудно ответить, не зная, что вы пытаетесь проверить. Тем не менее, вы не должны полагаться на порядок при использовании GetInterfaces , и этот метод вернет пустой массив, если тип не реализует его, поэтому проверка на null не требуется.

Изменить: если вы действительно не можете изменить утверждения, безопаснее всего сделать следующее:

        Type[] allInterfaces = typeof(Foo).GetInterfaces();
        var interfaces = allInterfaces.Where(x => x == typeof(IEnumerable<int>)).ToArray();

        Debug.Assert(interfaces != null);
        Debug.Assert(interfaces.Length == 1);
        Debug.Assert(interfaces[0] == typeof(IEnumerable<int>));
0
ответ дан 5 December 2019 в 12:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: