Aarrgh… here’s a task that should be easy but is dreadfully convoluted: Using Reflection, try and retrieve only the declared Interfaces of a type. By declared I mean only those implemented by the type, not interfaces Inherited. Reflection makes retrieval of types and interfaces very easy with the GetInterfaces() method. Unfortunately GetInterfaces() doesn’t provide a lot of flexibility in what gets retrieved.
In Help Builder, one of the things the Type and Assembly importing does, is retrieve all types defined and it retrieve the inheritance list of classes as well as the interface implemented.
Help Builder does all sorts of stuff with Reflection to retrieve type information including methods, fields, properties events etc. Reflection is very powerful and it makes it really straight forward to retrieve all of this information. What’s even nicer is that in most cases you can apply filters with BindingFlags that let you determine exactly what to return.
For example, in Help Builder I give people the choice when importing their classes to only import members that are declared at the current class hierarchy level. For example, when importing declared only types I can do:
this.RetrievalFlags = BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
These BindingFlags can be applied to GetMembers() and GetMethods(),GetFields(), GetProperties() etc. and it works great.
You can also retrieve Interfaces with the GetInterfaces() method. GetInterfaces() returns a complete list of interfaces that are implemented by the class – which if you subclass any control based classes can be a damn long list of interfaces! In Help Builder this is just not right:
What I really want on my imports is to only show the actively declared interfaces of the class I’m importing.
Unfortunately this GetInterfaces() does not support these binding flags and the Interfaces returned give no indication at which level they are implemented.
Two hours of playing around with a number of different approaches (Comparing the base Interface and the current interface, running through each interface looking for matching methods in my declared methods etc.) Besides being ugly they are not solving the problem completely.
It turns out there’s a better way that’s fairly easy and not totally resource intensive using an InterfaceMapping. Yeah, I had no idea what an InterfaceMap is either <g>. In fact, I’m still not quite sure what this class does beyond the one thing I need it for – it holds references to a bunch of information that I can get from the Type and the Interface easily on my own.
Anyway the following C# code demonstrates how to retrieve a list of interfaces from a type and then retrieve only the implemented interfaces:
Type[] Implementations = loType.GetInterfaces();
if (Implementations!=null)
{
foreach(Type Implementation in Implementations)
{
// *** This will work AS LONG AS THE INTERFACE HAS AT LEAST ONE MEMBER!
// *** This will give not show an 'empty' placeholder interface
// *** Can't figure out a better way to do this...
InterfaceMapping im = loType.GetInterfaceMap(Implementation);
if (im.TargetMethods.Length >0 &&
im.TargetMethods[0].DeclaringType == loType)
loObject.cImplements += Implementation.Name + ",";
}
if (loObject.cImplements != "")
loObject.cImplements = loObject.cImplements.Substring(0,loObject.cImplements.Length-1);
}
Now unfortunately this is also not a 100% solution as it looks at the TargetMethods collection. The assumption here is that the interface has at least one method or property. If you have a placeholder interface that defines no properties or methods there won’t be any TargetMethods and this code will not find this interface as a declared interface.
Maybe there's a better way, but while searching I couldn't find a solution, only a few others asking the same question. If anyone has a better solution I’m all ears. In the meantime this handles the majority of cases and I can look forward to a much smaller Interface display in Help Builder.
Now the interface list is imported correctly:
BTW, if you are a Help Builder user you’ll notice that there have been a number of enhancements to the .NET class importer. A couple of the new features – that admittedly should have been there before – are links to the inheritance list, including MSDN links to .NET types, proper type declarations instead of the IDL names previously used. In addition, the XML Doc parser now properly parses <see> CREF tags and links them either to internal topics or MSDN topics. I've also updated the layout of the stylesheet and the base templates to be closer to MSDN style (although they aren't using the exact same style). Yeah, I know – just slowly catching up to NDoc, but remember Component documentation is only part of Help Builder as it is a full Help and documentation development environment that allows full editing for content. Developer docs need more than just imported documentation that comes from source code.
These new features will be in the next drop in the next few days.
Other Posts you might also like