Warm tip: This article is reproduced from serverfault.com, please click

Is it possible to test for an interface implementation using EnvDTE.ClassInfo objects

发布于 2013-04-16 21:26:54

All,

I have a T4 template that generates boiler-plate code that handles my property-changed notification and automatically registers dependancyproperties for me based on attributes I have assigned to the class. I accomplish this using EnvDTE to walk up and down the project and retrieve an IEnumerable of ClassInfo objects. I then enumerate through the ClassInfo.Attributes to retrieve ClassInfo objects that have certain custom attributes I created (i.e. INotifyPropertyChangedAttributeAttribute:System.Attribute) with all the relavent information I need to have the template write the boiler-plate code for me.

Now, my question is, is it possible to (using EnvDTE) check for an Interface implementation (such as INotifyPropertyChanged) which might be inheritied from a base class so that I don't end up with two PropertyChanged events in my class (one in the inherited class and one in the code-generated partial class)?

For examle:

public class vmBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null) PropertyChanged(this, e);
    }
}

[INotifyPropertyChangedAttribute(Test1, typeof(string))] //NOTE: By including this attribute, T4 template will automatically generate properties.  What I need to know, though, is if the EnvDTE.ClassInfo can show Internface implementations as well so that I don't recreate the INotifyPropertyChanged Event
public partial class vm: vmBase //Implements INotifyPropertyChanged
{
    //....
}

[INotifyPropertyChangedAttribute(Test2, typeof(string))]
public partial class SomeClassThatDoesNotImplementInotifyPropertyChangedAlready
{
    //....
}

Hopefully that makes some sense.

See http://www.scottlogic.co.uk/blog/colin/2009/08/declarative-dependency-property-definition-with-t4-dte/ for an example of using envDTE and T4 to take care of dependancyproperty registrations. Concepts in my project are the same, only I'm adapting it to handle INotifyPropertyChanged boiler-plate code.

Thanks in advance.

Questioner
William
Viewed
0
Nico 2013-04-17 06:47:02

It took me a little, but yes - there is a way to find out via EnvDTE if a given class somehow inherits a given interface.

This code fragment only detects classes that inherit directly from another class that implements INotifyPropertyChanged. So before using it, one would add some recursive logic here...

<#
// get a reference to the project of this t4 template
var project = VisualStudioHelper.CurrentProject;
// get all class items from the code model
var allClasses = VisualStudioHelper.GetAllCodeElementsOfType(project.CodeModel.CodeElements, EnvDTE.vsCMElement.vsCMElementClass, false);

// iterate all classes
foreach(EnvDTE.CodeClass codeClass in allClasses)
{
    // get all interfaces implemented by this class

    var allInterfaces = VisualStudioHelper.GetAllCodeElementsOfType(codeClass.ImplementedInterfaces, EnvDTE.vsCMElement.vsCMElementInterface, true);
    if (allInterfaces.OfType<EnvDTE.CodeInterface>()
                     .Any(i => i.Name == "INotifyPropertyChanged"))
    {
        #>Implements Interface Directly: <#= codeClass.FullName #>
        <#

        // find classes that derive from this code class
        foreach(EnvDTE.CodeClass potentialDerivingClass in allClasses)
        {
            IEnumerable<string> theBases = VisualStudioHelper.GetAllCodeElementsOfType(potentialDerivingClass.Bases, EnvDTE.vsCMElement.vsCMElementClass, true).OfType<EnvDTE.CodeClass>().Select(cc => cc.FullName);
            if (theBases.Any(b => b == codeClass.FullName))
            {
                #>Derives from implementing class: <#= potentialDerivingClass.FullName #>
                <#
            }
        }
    }
}
#>

So given a class A that implements INotifyProperty changed, a class B deriving from A and another class C deriving from B, this code would come up with classes A and B that implement INotifyPropertyChanged.

Note: Because using the EnvDTE classes is not that nice, I used a reusable template from tangible T4 Editor's free template gallery named "tangible Visual Studio Automation Helper" - that makes it much easier to use!