Respuesta propuesta Finding Interfaces implemented in a Class

  • Monday, April 30, 2012 1:48 PM
     
     

    I want to find out what Interfaces a ClassTypeDecl implements. I'm thinking that this is best done with a Semantic call. I want to find if a class implements a specific Interface.

    There is a caveat - I need this to work if the Interface is defined 'in-class' or in a base class??

All Replies

  • Monday, April 30, 2012 3:29 PM
     
     Proposed Has Code

    Yes, using semantics is the right thing to do here. If you create a semantic model for a syntax tree, you can then ask it for a NamedTypeSymbol for a ClassDeclarationSyntax (I'm not sure what ClassTypeDecl is). And this NamedTypeSymbol then has a property AllInterfaces which will give you symbols for all the interfaces this class implements, including interfaces implemented though base classes or through other interfaces.

    For example like this:

    string code =
        @"
    interface IFoo
    {}
    
    interface IBar
    {}
    
    class Foo : IFoo
    {}
    
    class Bar : Foo, IBar
    {}";
    
    var tree = SyntaxTree.ParseCompilationUnit(code);
    var bar = tree.Root.DescendentNodes().OfType<ClassDeclarationSyntax>().Last();
    
    var compilation = Compilation.Create("foo")
        .AddSyntaxTrees(tree);
    
    var semanticModel = compilation.GetSemanticModel(tree);
    
    var barSymbol = semanticModel.GetDeclaredSymbol(bar);
    
    var interfaces = barSymbol.AllInterfaces; // contains IFoo and IBar

  • Tuesday, May 01, 2012 8:28 PM
     
      Has Code

    Hi,

     Many thanks for the input on this I've got a work around at the moment but I am not happy with it. It basically checks what the basetype is and if its the one I am after I run with that. Of course in real life I will not know how deep the Interface I won't could potentially be so I really want my code to work like the example posted. I've run the above code and it works just as I would expect. This leaves me a little puzzled as to why the following is not working -:

                string code = @"    public class ViewModelBase : INotifyPropertyChanged
                                    {
    
                                        #region INotifyPropertyChanged Members
    
                                        public event PropertyChangedEventHandler PropertyChanged;
    
                                        #endregion
                                     }";
    
    //                            @"
    //                            interface IFoo
    //                            {}
    //
    //                            interface IBar
    //                            {}
    //
    //                            class Foo : IFoo
    //                            {}
    //
    //                            class Bar : Foo, IBar
    //                            {}";
    
                var tree = SyntaxTree.ParseCompilationUnit(code);
                var bar = tree.Root.DescendentNodes().OfType<ClassDeclarationSyntax>().Last();
    
                var compilation = Compilation.Create("foo").AddSyntaxTrees(tree).AddReference(new AssemblyFileReference(typeof(System.ComponentModel.INotifyPropertyChanged).Assembly.Location));
    
                var semanticModel = compilation.GetSemanticModel(tree);
    
                var barSymbol = semanticModel.GetDeclaredSymbol(bar);
    
                var interfaces = barSymbol.AllInterfaces; // contains IFoo and IBar

    The above is pretty much the same as the previous example but with a small class implementing the interface INotifyPropertyChanged. It also adds the assemblies required to know about INotifyPropertyChanged.

    This returns me an empty set for 'interfaces'.... Whats up?

  • Tuesday, May 01, 2012 9:05 PM
     
      Has Code

    I think you're missing

    using System.ComponentModel;

    before declaring ViewModelBase.

    You can use compilation.GetDiagnostics() to see whether the compilation contains any errors or warnings.

  • Wednesday, May 02, 2012 8:50 AM
     
      Has Code

    svick - thanks for helping me with this. Adding the using to the code string the posted above solved the issue. Thanks for the tip on GetDiagnostics - I'll remember that for compilations.

    There is a very weird problem. I am iterating through a solution and its associated projects , looking for projects containing C# files and then trying to see if that file implements the interface I'm interested. When I inline the compilation (ie I don't call out to a function to do the compilation) it fails to find the interfaces. Yet when I implement the interface finding code as a method it works well/flawlessly!

    As a work around - I've implemented the following private method -: 

    private bool LookForInterface(string fileContents, string interfaceName, out List<ClassDeclarationsSyntax> aListOfSyntax);

    When calling this method its behaving as I want it and would expect. Weirdly if inline this code (ie instead of calling the private method) it returns zero for the interfaces. Because I've been getting so much help from people on here I'm going to try and replicate this in a small project - if I can replicate then I'll post it up.


    Rich

  • Wednesday, May 02, 2012 9:15 AM
     
     
    Are you sure that's what's actually happening and not just what you see in the debugger? I had some issues with Roslyn where the Visual Studio debugger showed a collection as empty, but it actually contained some elements. Maybe this is a similar issue?
  • Wednesday, May 02, 2012 10:57 AM
     
      Has Code

    svick,  I was tempted to think that and yes I have seen LOTS of issues like this. My situation is that I am debugging via VS2010 when a plugin is loaded into Expression Blend.

    The plugin contains the Roslyn code.

    This seems to be causing a lot of threading issues and sometimes I am simply losing context within a Debuging phase. :(

    However I have done things like set up conditions to see if my code is ever hit should it encounter ANY interfaces. Code such as -:

    var Interfaces = barSymbol.AllInterfaces; 
    
    foreach(var interfaceFound in Interfaces)
    {
     //PUT A BREAKPOINT ON THIS LINE
     Debug.Writeline("WOO HOO - we found an interface!");
    }
    
    
    And I don't see it. Ever. So something odd is going on.