C#
generics
interfaces
programming
type-checking

How to determine if a type implements a specific generic interface type

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In C sharp reflection scenarios, you often need to know whether a runtime type implements a generic interface such as IRepository<T> or IHandle<TCommand>. Direct type equality checks are not enough because concrete implementations are closed generic variants. The correct approach is to inspect implemented interfaces and compare generic type definitions.

Closed Versus Open Generic Interfaces

Given a generic interface:

csharp
1public interface IRepository<T>
2{
3    T GetById(int id);
4}

A concrete type might implement IRepository<Customer>. At runtime, you typically want to ask whether it implements IRepository<> for any type argument.

Reflection Helper Method

Use a reusable helper that handles both direct and inherited interfaces.

csharp
1using System;
2using System.Linq;
3
4public static class TypeExtensions
5{
6    public static bool ImplementsGenericInterface(this Type candidate, Type genericInterface)
7    {
8        if (candidate == null) throw new ArgumentNullException(nameof(candidate));
9        if (genericInterface == null) throw new ArgumentNullException(nameof(genericInterface));
10
11        if (!genericInterface.IsInterface || !genericInterface.IsGenericTypeDefinition)
12            throw new ArgumentException("genericInterface must be an open generic interface type definition");
13
14        return candidate
15            .GetInterfaces()
16            .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == genericInterface);
17    }
18}

Usage:

csharp
var result = typeof(CustomerRepository).ImplementsGenericInterface(typeof(IRepository<>));
Console.WriteLine(result);

Example Types

csharp
1public class Customer { }
2
3public class CustomerRepository : IRepository<Customer>
4{
5    public Customer GetById(int id) => new Customer();
6}
7
8public class PlainType { }

Checks:

csharp
Console.WriteLine(typeof(CustomerRepository).ImplementsGenericInterface(typeof(IRepository<>))); // true
Console.WriteLine(typeof(PlainType).ImplementsGenericInterface(typeof(IRepository<>))); // false

This works regardless of concrete type argument.

Matching a Specific Closed Generic Interface

If you need exact closed type match, compare directly.

csharp
1var exact = typeof(CustomerRepository)
2    .GetInterfaces()
3    .Contains(typeof(IRepository<Customer>));
4
5Console.WriteLine(exact);

Use open-generic check for category matching, closed-generic check for exact contract matching.

Handling Generic Base Classes

Sometimes interfaces are inherited through base classes. GetInterfaces includes inherited interfaces, so helper still works. If you also need generic base-class matching, add BaseType traversal logic separately.

csharp
1public static bool InheritsOpenGenericClass(Type candidate, Type openGenericBase)
2{
3    while (candidate != null && candidate != typeof(object))
4    {
5        var current = candidate.IsGenericType ? candidate.GetGenericTypeDefinition() : candidate;
6        if (current == openGenericBase) return true;
7        candidate = candidate.BaseType;
8    }
9    return false;
10}

This complements interface checks in plugin-style systems.

Performance Considerations

Reflection can be expensive in hot paths. Cache results when checking many types repeatedly.

csharp
1using System.Collections.Concurrent;
2
3public static class TypeCache
4{
5    private static readonly ConcurrentDictionary<(Type, Type), bool> Cache = new();
6
7    public static bool ImplementsGenericInterfaceCached(Type candidate, Type genericInterface)
8    {
9        return Cache.GetOrAdd((candidate, genericInterface), key =>
10            key.Item1.ImplementsGenericInterface(key.Item2));
11    }
12}

Caching makes startup scanning and DI container registrations faster.

Dependency Injection Registration Example

This check is useful when auto-registering handlers in a dependency injection container.

csharp
1var handlerTypes = AppDomain.CurrentDomain.GetAssemblies()
2    .SelectMany(a => a.GetTypes())
3    .Where(t => !t.IsAbstract && t.ImplementsGenericInterface(typeof(IRepository<>)))
4    .ToList();
5
6foreach (var t in handlerTypes)
7{
8    Console.WriteLine(t.FullName);
9}

This pattern helps build plugin-like systems where implementations are discovered at runtime. It is also common in mediator-style command pipelines and message handlers. Reflection filters simplify modular architecture bootstrapping. They improve startup clarity.

Testing Reflection Helpers

Unit tests should include:

  • Type directly implementing interface.
  • Type inheriting implementation from base class.
  • Type implementing unrelated interface.
  • Invalid input where interface argument is not open generic.

This keeps helper behavior stable as codebase evolves.

Common Pitfalls

  • Comparing interface type directly to open generic definition. Fix by calling GetGenericTypeDefinition on implemented interfaces.
  • Passing closed generic type when helper expects open interface type. Fix by validating IsGenericTypeDefinition.
  • Ignoring inherited interfaces. Fix by using GetInterfaces, not only direct declarations.
  • Running reflection checks repeatedly without cache. Fix by memoizing results in concurrent dictionary.
  • Mixing class inheritance checks with interface checks. Fix by handling generic base classes separately.

Summary

  • Use reflection to inspect implemented interfaces and compare open generic definitions.
  • Open generic checks answer category questions such as IRepository<> support.
  • Closed generic checks answer exact contract questions.
  • Add caching when checks run frequently.
  • Validate helper inputs to avoid silent false negatives.

Course illustration
Course illustration