2

全部,

我有一个 T4 模板,它生成样板代码来处理我的属性更改通知,并根据我分配给类的属性自动为我注册依赖属性。我使用 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
{
    //....
}

希望这有点道理。

有关使用 envDTE 和 T4 处理依赖属性注册的示例,请参见http://www.scottlogic.co.uk/blog/colin/2009/08/declarative-dependency-property-definition-with-t4-dte/ 。我的项目中的概念是相同的,只是我正在对其进行调整以处理 INotifyPropertyChanged 样板代码。

提前致谢。

4

4 回答 4

1

另一种方法(可能更健壮)是利用CodeClass接口的IsDerivedFrom属性。例如,您的模板可能如下所示:

<#@ 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);
    }
  }
#>
于 2015-10-12T23:22:03.450 回答
0

我花了一点时间,但是是的——有一种方法可以通过 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 #>
                <#
            }
        }
    }
}
#>

因此,假设一个实现 INotifyProperty 的类 A 发生了变化,一个从 A 派生的 B 类和另一个从 B 派生的类 C,此代码将提出实现 INotifyPropertyChanged 的​​类 A 和 B。

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

于 2013-04-16T22:47:02.113 回答
0

可能有更好的方法,但我决定做的是创建一个新属性

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

这样,我可以在不从根本上改变我的 T4 模板的情况下测试该属性是否存在。但是,如果有更好的方法,我会很乐意将其标记为答案。

于 2013-04-16T22:16:05.863 回答
0

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

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

此代码用于查找已实现的类。

于 2021-01-27T09:23:09.403 回答