开发者

#include directive in C#

开发者 https://www.devze.com 2022-12-18 12:52 出处:网络
Is there a replacement? If开发者_如何学Go there is, how would the directive look for a file named \"class.cs\"?

Is there a replacement? If开发者_如何学Go 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.


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


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.


Unlike C or C++ in C# there is no need to have a #include to use types defined in other files. C# instead does type resolution based on containers such as classes or namespaces. As long as both files are included in the compilation and the namespace of the second type is used in is available then your class will be accessible.

Example:

Class1.cs

namespace Project1 { 
 class Class1 { ... }
}

Class2.cs

namespace Project1 {
  class Class2 {
    private Class1 m_field1;
    ..
  }
}


Also don't forget C# partial classes have some functionality that you might otherwise get with #include statements.

Partial classes allow you to split a class definition over several files.


It's a little unclear what you mean. But are you thinking about:

using MyNamespace;


It's not exactly like C's #include directive, but C#'s using statement is what you're after:

using Assembly.Name;

It works at a namespace level, rather than a file level. So, if class.cs includes a public class named SomeClass in the Application.Core namespace, this would look like:

using Application.Core;

This is typically placed at the top of the file you are working in and would allow that class to use SomeClass as an object (along with all other public classes in the Application.Core namespace).

Of course, if the classes are all in the same namespace (e.g. Application.Core) there's no reason to employ the using statement at all. Classes within the same namespace can resolve each other without any declarations.


You include each file in the *.csproj if you are using msbuild or in the csc (C# Compiler) command line:

csc File1.cs File2.cs

http://msdn.microsoft.com/en-us/library/78f4aasd%28VS.80%29.aspx


check out the using statement


You would need to place the contents of class.cs into a namespace. And then put a using statement at the top of the file that needs to see class.cs.

class.cs
namespace Class {
//class.cs stuff
}

Then do the following in the file that needs class.

  using Class;


an example using Partial Class.

Main.cs

partial class Program
{
    private static void Main()
    {
        A();
        B();
    }
}

fileA.cs

partial class Program
{
    private static void A() => Console.WriteLine("A");
}

fileB.cs

partial class Program
{
    private static void B() => Console.WriteLine("B");
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号