如何获取类实现的INotificationHandler<>的泛型参数类型?
Great question! Using GenericTypeArguments[0] works in simple cases, but it's fragile—it relies on the INotificationHandler<> interfaces being the first generic interfaces implemented by your class. If you ever add another generic interface (or reorder them), this will break unexpectedly.
Here's a much safer approach that explicitly targets the INotificationHandler<> interface, regardless of how many other interfaces your class implements or their order:
using System; using System.Collections.Generic; using System.Linq; public static class TypeExtractor { public static IEnumerable<Type> MagicFunctionToGetTemplateTypesForNotificationHandlerInterface(IEnumerable<Type> typesList) { // Define the open generic type we're searching for var handlerGenericDefinition = typeof(INotificationHandler<>); foreach (var type in typesList) { // Filter interfaces to only closed versions of INotificationHandler<> var matchingInterfaces = type.GetInterfaces() .Where(iface => iface.IsGenericType && iface.GetGenericTypeDefinition() == handlerGenericDefinition); foreach (var iface in matchingInterfaces) { // Extract the single generic argument (INotificationHandler<> only has one) var genericArgs = iface.GetGenericArguments(); if (genericArgs.Length == 1) // Extra safety guard against unexpected interface changes { yield return genericArgs[0]; } } } } }
Why this is safer:
- Explicit targeting: We only process interfaces that match the
INotificationHandler<>open generic type, so other generic interfaces (likeIEnumerable<>or custom ones) won't interfere. - Order-agnostic: It doesn't matter if
INotificationHandler<>is the first interface or the tenth—we'll find it regardless. - Robust to changes: If you modify the class to add more interfaces later, this code won't break silently.
- Safety check: The extra check for
genericArgs.Length == 1adds a layer of protection in case theINotificationHandlerinterface ever changes (unlikely, but it's a cheap guardrail).
What's wrong with the original approach?
Suppose you later update your class to:
public class OtherClass : IAnotherGeneric<RandomType>, INotificationHandler<Aggregate>, INotificationHandler<Quote> { /* */ }
Using GetType().GenericTypeArguments[0] would now return RandomType instead of Aggregate—a silent bug that's hard to track down. The safe approach avoids this entirely.
Testing it out
With your original typesList = new[] { typeof(OtherClass) }, calling MagicFunctionToGetTemplateTypesForNotificationHandlerInterface(typesList) will correctly return typeof(Aggregate) and typeof(Quote) (order depends on interface enumeration, but both will be included).
内容的提问来源于stack exchange,提问作者morleyc

