3

Here's what I'd like to do.

I want to write POCO classes like this:

[AutoExtended]
public partial class Foo {
    public int Bar;
    public string Baz;
}

preferably in arbitrary files in my solution (the [AutoExtend] attribute is something I just made up to identify the classes of interes).

I want the build process to start by (a) looking for these AutoExtend classes in my source code and (b) automatically generate extensions like this:

public partial class Foo {
    public static SomeType<int> Bar(Foo x) { ... };
    public static SomeOtherType<string> Baz(Foo x) { ... };
}

before compiling the solution.

Does anyone know how best to do this? I imagine Roslyn is the way to go, but I'm open to advice. Ideally, I'd like a solution which requires zero extra "plumbing" on the part of the user other than the AutoExtend attribute.

(In case anyone is interested, I'm writing a domain specific language in terms of C# classes with overloaded operators and the above would make the DSL substantially more comfortable to use.)

Rafe
  • 5,237
  • 3
  • 23
  • 26
  • So you want to do this as a build step? Because that could be tricky considering source control, continuous integration builds etc. Would T4 be too awkward to use? I suppose instead of building the project the user would have to run the T4 template. Interesting question either way. – Alex Paven Sep 06 '13 at 09:14
  • Hi, yes I'd prefer a build step because I have an aversion to adding generated files to source control - that leads to all sorts of grief. T4 would be ideal for the output part, what I'm unclear on is (a) how best to process the source code first and (b) how to integrate this processing as a build step. – Rafe Sep 06 '13 at 23:05

1 Answers1

6

As suggested in the comments, it's quite feasible with T4.

With regard to transforming on build, you can do it with a <TransformOnBuild> property in the .csproj file. see this question, in particular @Cheburek's answer. There is more information on MSDN.

Then to locate the classes with the AutoExtend attribute you'd need to use EnvDTE instead of reflection, because any existing assembly would be out of date.

Something like:

<#
// 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 attributes this method is decorated with
        var allAttributes = VisualStudioHelper.GetAllCodeElementsOfType(codeClass.Attributes, vsCMElement.vsCMElementAttribute, false);
        // check if the SomeProject.AutoExtendedAttribute is present
        if (allAttributes.OfType<EnvDTE.CodeAttribute>()
                         .Any(att => att.FullName == "SomeProject.AutoExtended"))
        {
        #>
        // this class has been generated
        public partial class <#= codeClass.FullName #>
        {
          <#
          // now get all methods implemented by the class
          var allFunctions = VisualStudioHelper.GetAllCodeElementsOfType(codeClass.Members, EnvDTE.vsCMElement.vsCMElementFunction, false);
          foreach(EnvDTE.CodeFunction function in allFunctions)
          {
          #>
              public static <#= function.FullName #> etc...
          <#
          } 
          #>
        }
<#          
        }
    }
}
#>
shamp00
  • 11,106
  • 4
  • 38
  • 81
  • This looks like just the ticket! Many thanks, I'll give it a try and let you know how it goes. – Rafe Sep 07 '13 at 13:47