Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

#include directive in C#

Tags:

c#

Is there a replacement? If there is, how would the directive look for a file named "class.cs"? I just want to split the code into a file for each class.

like image 814
Rom C. Avatar asked Jan 25 '10 17:01

Rom C.


2 Answers

The idiomatic way to achieve metaprogramming in C# (beyond Generics) is with T4 templates - Visual Studio and MSBuild supports T4 built-in, however VS does not come with T4 syntax coloring - you'll need a third-party add-in for that.

In order to demonstrate T4's include functionality, I'll use the scenario of wanting to add an == operator overload to multiple classes simultaneously without using inheritance.

For comparison, in C++ it would be like this:

OperatorEquals.inc

bool operator==(const TYPE* lhs, const TYPE* rhs) { if( lhs == nullptr && rhs != nullptr ) return false; return lhs.Equals(rhs); }

Code.h

class Foo {
public:
#define TYPE Foo
#include "OperatorEquals.inc"
}

class Bar {
public:
#define TYPE Bar
#include "OperatorEquals.inc"
}

In C#, you would do this:

  1. Use partial classes so that all of your non-metaprogramming logic (i.e. normal C# code) is in a file, e.g. Foo.cs and Bar.cs
  2. Create a new T4 template in your project, change the output file extension to .cs
  3. Create a second partial class definition of the same type within that T4 (*.tt) file, though you won't have C# syntax highlighting.
  4. Define the included file:

Operators.inc.cs.t4

public static operator==(<#= typeName #> x, <#= typeName #> y) {
    if( x == null && y != null ) return false;
    return x.Equals( y );
}
  1. Add it to your T4 template:

Metaprogramming.tt

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ import namespace="System" #>
<#@ output extension=".cs" #>
<# String typeName = null; #>

public partial class Foo {

    <# typeName = "Foo"; #>
    <#@ include file="Operators.inc.cs.t4" #>
}

public partial class Bar {

    <# typeName = "Bar"; #>      
    <#@ include file="Operators.inc.cs.t4" #>
}

Whenever you "Save" the .tt file (even if you make no changes) VS will regenerate the output .cs file which will look like this:

public partial class Foo {

    public static operator==(Foo x, Foo y) {
       if( x == null && y != null ) return false;
       return x.Equals( y );
   }
}

public partial class Bar {

   public static operator==(Bar x, Bar y) {
       if( x == null && y != null ) return false;
       return x.Equals( y );
   }
}

Note that this scenario is contrived - if you really did want to add the operator== (and all the others: IEquatable<T>, operator!=, IComparable<T>, etc) then you would probably use a T4 render function instead of an include, because that makes parameterization more straightforward and keeps everything self-contained in a single file:

T4RenderFunction.tt

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ import namespace="System" #>
<#@ output extension=".cs" #>
<# String typeName = null; #>

public partial class Foo {

    <# RenderBoilerplateOperators("Foo"); #>
}

public partial class Bar {

    <# RenderBoilerplateOperators("Bar"); #>
}

<#+
// Functions are declared at the bottom
void RenderBoilerplateOperators(String typeName) {
#>
    public static operator==(<#= typeName #> lhs, <#= typeName #> rhs) {
        return <#= typeName #>.Equals( lhs, rhs );
    }

    public override Boolean Equals(<#= typeName #> other) {
        return <#= typeName #>.Equals( this, other );
    }

    public static Boolean Equals(<#= typeName #> lhs, <#= typeName #> rhs) {
        // T4 can use VS DTE to enumerate members of `typeName`, but you're probably better-off implementing this method manually
    }

    public static operator!=(<#= typeName #> lhs, <#= typeName #> rhs) {
        return !<#= typeName #>.Equals( lhs, rhs );
    }

    // and so on...
<#
} // void RenderBoilerplateOperators
#>
like image 79
Dai Avatar answered Oct 27 '22 20:10

Dai


No, there is no replacement for an #include statement. C# is an object-oriented language where code is organised into classes. You can use code from one class in another class depending on its visibility, and you can split the code from a single class across multiple source files using partial classes. These are essentially the way you use code from one "file" in another. But it's not the same thing at all.

like image 37
David M Avatar answered Oct 27 '22 20:10

David M