代码之家  ›  专栏  ›  技术社区  ›  William

是否可以使用EnvDTE.ClassInfo对象测试接口实现

  •  2
  • William  · 技术社区  · 11 年前

    全部的

    我有一个T4模板,它生成锅炉板代码,处理我的属性更改通知,并根据我分配给类的属性自动为我注册dependencyproperty。我使用EnvDTE在项目中来回走动并检索ClassInfo对象的IEnumerable来实现这一点。然后,我通过ClassInfo.Attributes进行枚举,以检索具有我创建的某些自定义属性(即INotifyPropertyChangedAttributeAttribute:System.Attribute)的ClassInfo对象,以及我需要的所有相关信息,以便让模板为我写锅炉板代码。

    现在,我的问题是,是否可以(使用EnvDTE)检查可能从基类继承的接口实现(如INotifyPropertyChanged),这样我就不会在类中出现两个PropertyChanged事件(一个在继承的类中,一个在代码生成的分部类中)?

    例如:

    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
    {
        //....
    }
    

    希望这有点道理。

    看见 http://www.scottlogic.co.uk/blog/colin/2009/08/declarative-dependency-property-definition-with-t4-dte/ 例如使用envDTE和T4来处理dependencyproperty注册。我的项目中的概念是相同的,只是我正在调整它来处理INotifyPropertyChanged锅炉板代码。

    提前谢谢。

    4 回复  |  直到 11 年前
        1
  •  1
  •   erdomke    9 年前

    另一种选择(可能更稳健)是利用 IsDerivedFrom 的财产 CodeClass 界面例如,您的模板可能看起来像:

    <#@ template  debug="true" hostSpecific="true" #>
    <#@ output extension=".cs" #>
    <#@ Assembly Name="System.Core" #>
    <#@ assembly name="EnvDte" #>
    <#@ import namespace="System" #>
    <#@ import namespace="System.IO" #>
    <#@ import namespace="System.Diagnostics" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Collections" #>
    <#@ import namespace="System.Collections.Generic" #> 
    <#    
      foreach(var ns in GetNamespaceElements())
      {
        foreach(var cc in ns.Members.OfType<EnvDTE.CodeClass>())
        {
          if (cc.IsDerivedFrom["System.ComponentModel.INotifyPropertyChanged"]) {
    #>YOUR_CODE_HERE<#  
          }
        }
      }
    #>
    
    <#+
      public IEnumerable<EnvDTE.CodeNamespace> GetNamespaceElements()
      {
        var visualStudio = (this.Host as IServiceProvider).GetService(typeof(EnvDTE.DTE))
                            as EnvDTE.DTE;
        var project = visualStudio.Solution.FindProjectItem(this.Host.TemplateFile)
                      .ContainingProject as EnvDTE.Project;
    
        var projItems = new List<EnvDTE.ProjectItem>();
        FillProjectItems(project.ProjectItems, projItems);
        var names = new HashSet<string>(projItems
          .Where(i => i.FileCodeModel != null)
          .SelectMany(i => i.FileCodeModel.CodeElements.OfType<EnvDTE.CodeElement>())
          .Where(e => e.Kind == EnvDTE.vsCMElement.vsCMElementNamespace)
          .Select(e => e.FullName));
    
        var codeNs = new List<EnvDTE.CodeNamespace>();
        FillCodeNamespaces(project.CodeModel.CodeElements.OfType<EnvDTE.CodeNamespace>(), codeNs);
    
        return codeNs.Where(ns => names.Contains(ns.FullName));
      }
    
      public void FillCodeNamespaces(IEnumerable<EnvDTE.CodeNamespace> parents, List<EnvDTE.CodeNamespace> all)
      {
        foreach (var parent in parents)
        {
          all.Add(parent);
          FillCodeNamespaces(parent.Members.OfType<EnvDTE.CodeNamespace>(), all);
        }
      }
    
      public void FillProjectItems(EnvDTE.ProjectItems items, List<EnvDTE.ProjectItem> ret)
      {
        if (items == null) return;
        foreach(EnvDTE.ProjectItem item in items)
        {
          ret.Add(item);
          FillProjectItems(item.ProjectItems, ret);
        }
      }
    #>
    
        2
  •  0
  •   Nico    11 年前

    我花了一点时间,但是的,有一种方法可以通过EnvDTE来确定给定的类是否以某种方式继承了给定的接口。

    此代码片段仅检测直接从实现INotifyPropertyChanged的另一个类继承的类。所以在使用它之前,我们会在这里添加一些递归逻辑。。。

    <#
    // 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 #>
                    <#
                }
            }
        }
    }
    #>
    

    因此,给定一个实现INotifyPropertyChanged的类a、一个从a派生的类B和另一个从B派生的类C,这段代码将产生实现INotifyPropertyChanged的类a和B。

    注意:因为使用EnvDTE类不是很好,所以我使用了有形T4编辑器的免费模板库中的一个可重复使用的模板,名为“有形Visual Studio Automation Helper”,这使它更容易使用!

        3
  •  0
  •   William    11 年前

    也许有更好的方法,但我决定创建一个新的属性

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class INotifyPropertyChangedInheretedAttributeAttribute : Attribute
    {
        public INotifyPropertyChangedInheretedAttributeAttribute():base()
        {
        }
    }
    

    这样,我就可以在不从根本上改变T4模板的情况下测试这个属性是否存在。不过,如果有更好的方法,我会很乐意把它作为答案。

        4
  •  0
  •   Furkan    4 年前

    作为所有解决方案的补充(我回答了,因为我还不能添加评论:()。我用过

     foreach (CodeInterface iface in cc.ImplementedInterfaces)
     {
         if(iface.Name == "INotifyPropertyChanged")
         {
               MessageBox.Show(cc.Name);
         }
      }
    

    此代码用于查找Implemented类。