From list of types A, B, C, ANotState, BNotState I want to get A, B,C that are marked with the interface. But if do get interfaces from ANotState or BNotState I've got only isState which is inherited from C (from ANotState in BNotState case).
Is there a way to know if the interface is implemented directly?
public class Program
{
public static void Main()
{
List<Type> types = new List<Type>{typeof(A), typeof(B), typeof(C), typeof(ANotState), typeof(BNotState)};
//Now remove from list types that are not implementing isState interface directly which means ANotState and BNotState
}
}
public interface isState{}
public class A : isState
{
}
public abstract class B : A, isState
{
}
public abstract class C : B, isState
{
}
public class ANotState : C
{
}
public class BNotState : ANotState
{
}
There's a solution which partially works. You can retrieve the interfaces implemented by the current type, then the interfaces implemented by its parent type, and then subtract those two sets. Like this:
public HashSet<Type> GetDirectlyImplementedInterfaces<T>()
{
// interfaces of the given type T
var allIfaces = typeof(T).GetInterfaces().ToHashSet();
// interfaces of its base type
var baseIfaces = typeof(T).BaseType.GetInterfaces().ToHashSet();
// subtract
allIfaces.ExceptWith(baseIfaces);
return allIfaces;
}
However, it would work only for the first occurrence of specifying the given interface in the inheritance hierarchy. For example, in your case for A
the method above returns isState
, but for both B
and C
it doesn not, becase the interface was first implemented by their parent type, i.e. A
.
So, instead of using a marker interface, I'd recommend specifying the same information via a non-inheritable custom attribute:
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class IsStateAttribute : Attribute { }
Then you can go with a method like this to check if a type T
represents a state or not:
public bool CheckIsState<T>()
{
return typeof(T).GetCustomAttribute<IsStateAttribute>() != null;
}
This solutions correctly recognizes A
, B
and C
as states, and ANotState
and BNotState
as not states.
In case you'd need some members declared in the state interface, just combine both approaches.