1

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
{

}
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
  • please provide the code you're working with. – iSR5 Jul 16 '22 at 16:54
  • @iSR5 List types = new List{typeof(C), typeof(ANotState), typeof(BNotState)}; //Now remove types that are not implementing isState directly (ANotState and BNotState) – Panakvina321 Jul 16 '22 at 16:59
  • Please update your question, with the code that you're you need help with. – iSR5 Jul 16 '22 at 17:00
  • @iSR5 ok I edited the question. Hope you can see it now. If not here's a link https://dotnetfiddle.net/BwyM0X – Panakvina321 Jul 16 '22 at 17:10
  • Interfaces are **_not_** inherited. An interface is a contact. It states what is expected to be implemented for a class. So your whole viewpoint and setup of the question is based on an incorrect assumption. – JHBonarius Jul 16 '22 at 20:46
  • 1
    This is probably an XY problem. You have a problem that you think you can solve with interfaces being specified for a specific class. So you ask us how to solve that. However what you think is the solution is not a good solution. If you give us the actual underlying problem you are trying to solve, we might be able to help you better. – JHBonarius Jul 16 '22 at 20:49
  • Why would you ever want to know this? If a class supports the interface then it supports it, why do you care how it does so? – Charlieface Jul 17 '22 at 02:15
  • @JHBonarius I already solved the problem with attributes. The problem was that I'm developing state machine for my game where class lets say Entity can inherit class RunningState and then override methods like OnStateEnable - means Entity is running. But also you can have sub classes like RunningState can inherit AliveState so if you change state of Entity to RunningState also you are changing it to AliveState. Now class Player can inherit Entity but Entity is not StateClass. So the goal was to mark state classes and separate them from other inherited classes for state machine to work. – Panakvina321 Jul 17 '22 at 14:55
  • @Panakvina321 yes, it sounds like inheritance is not the proper solution there. You might want to look into SOLID, especially the single responsibility principle – JHBonarius Jul 17 '22 at 17:33

2 Answers2

5

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.

Ondrej Tucny
  • 27,626
  • 6
  • 70
  • 90
  • 1
    Thanks, I didn't think to use attributes. – Panakvina321 Jul 16 '22 at 20:08
  • That reminds me of my answer regarding the differences between marker interfaces and attributes: https://stackoverflow.com/a/3923829/245183 – Ondrej Tucny Jul 16 '22 at 20:10
  • Having to use reflection to achieve this just demonstrates how this is an XY problem – JHBonarius Jul 16 '22 at 20:51
  • @JHBonarius Using reflection demonstrates _exactly nothing_ with respect to something being an XY problem. – Ondrej Tucny Jul 16 '22 at 21:12
  • I disagree, over the years reviewing code and supporting juniors, i found that in most cases where somebody used or has to use reflection, they where actually approaching the problemen incorrectly. – JHBonarius Jul 17 '22 at 07:51
  • @JHBonarius Using attributes is **normal**. Attributes are here to carry metadata and enable many scenarios. You know nothing about the OPs scenario, and attributes alone are not a smell per se. – Ondrej Tucny Jul 17 '22 at 13:56
  • @JHBonarius On the other hand, I do agree that the “reflection hammer” is not a tool for beginners and they should rather look for simpler solutions. – Ondrej Tucny Jul 17 '22 at 13:59
  • _"You know nothing about the OPs scenario"_ q.e.d. the OP is not giving his scenario, instead he's asking us to help him solve what he thinks is the solution. It might be that his scenario has a better solution. But we don't know. Hence XY problem – JHBonarius Jul 17 '22 at 14:17
3

Are you sure you really need that? From a type perspective all the derived types implement that interface. Anyway if you really need that I don’t think there is a way to obtain this information using the System.Reflection (such as ImplementedInterfaces and GetInterfaces) as those include interfaces implemented by the base types as would things like is and as. If that is really required then you might need to use something like Mono.Cecil and read that from the actual metadata but then you have the overhead of reading in the file. See an example dotnetfiddle.

AndrewS
  • 6,054
  • 24
  • 31
  • Thanks for your work. I'm gonna use the attributes for that. It didn't come to my mind that attributes are just built for this job of marking something. But I'm gonna save you work. Maybe someday it will come handy :) – Panakvina321 Jul 16 '22 at 20:27