Как проверить, реализует ли тип определённый интерфейс, используя рефлексию в C#
Основной способ: метод IsAssignableFrom
Самый простой и надёжный способ — использовать метод Type.IsAssignableFrom(Type). Он проверяет, можно ли присвоить переменной типа IPlugin экземпляр класса MyPlugin. В контексте рефлексии это эквивалентно проверке, реализует ли тип интерфейс.
Правильный порядок аргументов: сначала тип интерфейса, потом тип проверяемого класса.
Type interfaceType = typeof(IPlugin);
Type classType = typeof(MyPlugin);
bool implementsInterface = interfaceType.IsAssignableFrom(classType);
Console.WriteLine(implementsInterface); // True
Этот метод работает для любых интерфейсов, включая обобщённые:
public interface IRepository<T> { }
public class UserRepository : IRepository<User> { }
Type openGenericInterface = typeof(IRepository<>);
Type closedGenericInterface = typeof(IRepository<User>);
Type repoType = typeof(UserRepository);
// Проверка для закрытого (конкретного) интерфейса:
bool closedCheck = closedGenericInterface.IsAssignableFrom(repoType); // True
// Проверка для открытого (неопределённого) интерфейса:
bool openCheck = openGenericInterface.IsAssignableFrom(repoType); // False!
Обратите внимание: IsAssignableFrom с открытым обобщённым типом (IRepository<>) вернёт false, так как класс реализует конкретную версию интерфейса, а не шаблон. Для проверки на соответствие любому обобщённому интерфейсу используйте GetInterfaces().
Альтернатива: метод GetInterfaces()
Метод Type.GetInterfaces() возвращает массив всех интерфейсов, которые реализует тип (включая унаследованные). Вы можете проверить, содержится ли нужный интерфейс в этом массиве.
Type classType = typeof(UserRepository);
Type targetInterface = typeof(IRepository<User>);
bool implements = classType.GetInterfaces()
.Any(i => i == targetInterface);
Console.WriteLine(implements); // True
Для проверки на открытый обобщённый интерфейс нужно сравнивать определения типов:
Type openGeneric = typeof(IRepository<>);
bool implementsOpen = classType.GetInterfaces()
.Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == openGeneric);
Console.WriteLine(implementsOpen); // True
Полный пример с обработкой null и наследованием
public static bool ImplementsInterface(Type type, Type interfaceType)
{
if (type == null || interfaceType == null)
throw new ArgumentNullException();
if (!interfaceType.IsInterface)
throw new ArgumentException("Второй аргумент должен быть интерфейсом");
// Прямая проверка через IsAssignableFrom
if (interfaceType.IsAssignableFrom(type))
return true;
// Если интерфейс обобщённый, проверяем через GetInterfaces
if (interfaceType.IsGenericType)
{
Type genericDefinition = interfaceType.GetGenericTypeDefinition();
return type.GetInterfaces()
.Any(i => i.IsGenericType &&
i.GetGenericTypeDefinition() == genericDefinition);
}
return false;
}
// Использование:
Console.WriteLine(ImplementsInterface(typeof(MyPlugin), typeof(IPlugin))); // True
Console.WriteLine(ImplementsInterface(typeof(UserRepository), typeof(IRepository<>))); // True
Важные замечания
- Производительность:
IsAssignableFromбыстрее, чемGetInterfaces(), так как не создаёт массив. Для однократной проверки разница незаметна, но в циклах используйте первый метод. - Наследование: оба метода учитывают цепочку наследования. Если базовый класс реализует интерфейс, то и производный класс будет считаться реализующим его.
- Интерфейсы с ограничениями: если интерфейс имеет дженерик-ограничения (where T : class), рефлексия всё равно проверяет только совместимость типов, не проверяя соблюдение ограничений.
Использование оператора is и приведение типов
Если у вас уже есть экземпляр объекта, а не только Type, можно использовать стандартные операторы C# — is или as. Это не чистая рефлексия, но часто более удобно в runtime-сценариях.
object instance = new MyPlugin();
// Способ 1: оператор is
if (instance is IPlugin plugin)
{
plugin.Execute(); // Можно сразу использовать
}
// Способ 2: оператор as
IPlugin? plugin2 = instance as IPlugin;
if (plugin2 != null)
{
plugin2.Execute();
}
Преимущества: код короче, не нужно работать с Type, и вы получаете сразу типизированную ссылку. Недостаток: требует наличия объекта. Если у вас только Type (например, полученный из сборки по имени класса), этот способ не подойдёт.
Дополнительно: проверка через Type.GetInterface()
Метод GetInterface(string name) ищет интерфейс по точному имени. Это может быть полезно, если имя интерфейса известно только в виде строки.
Type classType = typeof(MyPlugin);
Type? foundInterface = classType.GetInterface("IPlugin");
bool implements = foundInterface != null;
Обратите внимание: имя должно быть полным (с namespace) или совпадать с коротким именем, если интерфейс уникален. Для обобщённых интерфейсов имя будет содержать обратные кавычки, например IRepository`1.
Ответов пока нет.
Войдите чтобы оставить ответ