Type.GetInterfaces () only for declared interfaces

First of all, there are many questions like this, and perhaps some of the OPs even asked this same question. The problem is that the answer to these questions (accepted or not) does not actually answer this question, at least not to anyone that I can find.

How to define interfaces declared directly by the class and not those inherited by parents or declared interfaces?

eg.

interface I {} interface W : I {} class C : W {} class D : C, I {} class E : D {} 

Results:

  • C announces W
  • D announces I
  • E does not announce

A valid solution may require that the interfaces have at least one method.

If you think this is truly impossible, be careful not to make this mistake , which can actually be made .

InterfaceMap handles many cases, but not all (I gave an example below, not a solvable InterfaceMap ). One of my ideas, but I don’t know how to implement it, is to decompile the class bytecode and see what is declared, since tools like ILSpy correctly identify each case! If you like this idea, please give me a link to additional information in this area.

I expect some of you to advise me to clean my design. If this is not your argument, the rest of the message does not suit you.

Part of my project goal is to track the potential code paths of these types (at runtime). To programmatically determine which method will be called on the target type without actually calling the method or instantiating the target type, knowledge of the declared interfaces of the target type is necessary for a deterministic solution. "No," you say? Consider:

 interface I { int Foo(); } class C : I { public int Foo() { return 1; } } class D : C { public new int Foo() { return 2; } } class E : D, I { } C p = new E(); Assert.AreEqual(1 or 2, (p as I).Foo()) 

The correct answer is 2 , but if you change the declaration of E to not include I directly, the answer will be 1 . Now make sure this is the edge, but this is the correct answer. Therefore, my engine is not fully compatible with a potential user code. Telling users to clear their code to use my tool is unacceptable. (Note that there are dozens of more interesting rules for casting to the interface, but I will not discuss them here).

+4
source share
5 answers

Based on the helpful information from the comments, I was able to conclusively show that this cannot be done with reflection msft (although it could be with mono.cecil). The reason is that GetInterfaces calls create their own call, which returns interfaces that are previously flattened for the target type.

+2
source

To get only declared interfaces for this type, you can use GetInterfaces for this type, then if it has BaseType you can use the Except enumerator to exclude the interfaces of the base type ...

This has not been tested, but something like this extension method is possible ...

 public static IEnumerable<Type> GetDeclaredInterfaces(this Type t) { var allInterfaces = t.GetInterfaces(); var baseInterfaces = Enumerable.Empty<Type>(); if (t.BaseType != null) { baseInterfaces = t.BaseType.GetInterfaces(); } return allInterfaces.Except(baseInterfaces); } 
+2
source

How about this?

 Type type = typeof(E); var interfaces = type.GetInterfaces() .Where(i => type.GetInterfaceMap(i).TargetMethods.Any(m => m.DeclaringType == type)) .ToList(); 
+1
source

To programmatically determine which method will be called in the target type without actually calling the method or instantiating the target type, knowledge of the declared interfaces of the target type is necessary for a deterministic solution to this issue.

The Reflection API provides exactly this functionality with Type.GetInterfaceMap(Type) . For example, in your case typeof(E).GetInterfaceMap(typeof(I)) provides you with the following information that allows you to directly define the mapping:

 InterfaceMethods TargetMethods [0]: I.Foo() [0]: EIFoo() 

Clearly, TargetMethods tells you which method is called if the instance is of type E at run time.

You will notice that the interface method I.Foo() actually mapped to a method of type E that explicitly implements I.Foo() . This method was inserted by the compiler, and it just calls D.Foo() .

(Update: Unfortunately, as you can see in the screenshot, I made the second Foo() virtual. I get exactly the same result for non-virtual, though.)

+1
source

The problem is not that GetInterfaces gives incorrect information, I think. The problem is that you are looking for the wrong type.

When you have type code (p as I).Foo() , you should look at typeof(I) methods: not p.GetType() , not typeof(C) , not typeof(E) .

Reflection on I will tell you everything you need to properly resolve the call, although this is by no means trivial.

0
source

Source: https://habr.com/ru/post/1402516/


All Articles