Declarations

edit

Compilation units and namespaces

A CompilationUnit is a file. It contains using directives, namespaces and types. A NamespaceDeclaration can also have its own using directives and nested namespaces:

var unit = new CompilationUnit();
unit.Usings.Add(new UsingDirective("System"));
var ns = unit.AddNamespace("MyNamespace");
ns.AddType(new ClassDeclaration("MyClass"));
var code = unit.ToCsharpString();

Assert.StartsWith("using System;", code);
Assert.Contains("namespace MyNamespace", code);
Assert.Contains("class MyClass", code);
var ns = new NamespaceDeclaration("MyNamespace");
ns.Usings.Add(new UsingDirective("System"));
ns.AddType(new ClassDeclaration("MyClass"));
var code = ns.ToCsharpString();

Assert.StartsWith("namespace MyNamespace", code);
Assert.Contains("using System;", code);
Assert.Contains("class MyClass", code);

Types

ClassDeclares
ClassDeclarationA class, with an optional BaseType.
StructDeclarationA struct.
RecordDeclaration, RecordStructDeclarationA record class or a record struct.
InterfaceDeclarationAn interface.
EnumerationDeclarationAn enum, with its BaseType and its EnumerationMember items.
DelegateDeclarationA delegate, with its return type and arguments.

Every type has Modifiers and CustomAttributes. Classes, structs, records and interfaces have Members, generic Parameters and the interfaces they Implements:

var cls = new ClassDeclaration("MyClass");
cls.Modifiers = Modifiers.Public;
cls.BaseType = typeof(object);
cls.Members.Add(new MethodDeclaration("MyMethod") { Statements = [] });

Assert.Equal("""
    public class MyClass : object
    {
        void MyMethod()
        {
        }
    }

    """, cls.ToCsharpString());
var str = new StructDeclaration("Point");
str.Members.Add(new FieldDeclaration("X", typeof(int)));
str.Members.Add(new FieldDeclaration("Y", typeof(int)));

Assert.Equal("""
    struct Point
    {
        int X;

        int Y;
    }

    """, str.ToCsharpString());
var record = new RecordDeclaration("Person");

// An accessor without statements is auto-implemented
record.Members.Add(new PropertyDeclaration("Name", typeof(string)) { Getter = new PropertyAccessorDeclaration { Statements = null } });
record.Members.Add(new PropertyDeclaration("Age", typeof(int)) { Getter = new PropertyAccessorDeclaration { Statements = null } });

Assert.Equal("""
    record Person
    {
        string Name
        {
            get;
        }

        int Age
        {
            get;
        }
    }

    """, record.ToCsharpString());

Modifiers is a flags enum: combine the values with |, such as Modifiers.Public | Modifiers.Static | Modifiers.Partial.

Enums and delegates

var enumDecl = new EnumerationDeclaration("Status");
enumDecl.BaseType = typeof(int);
enumDecl.Members.Add(new EnumerationMember("Active", 1));
enumDecl.Members.Add(new EnumerationMember("Inactive", 2));

Assert.Equal("""
    enum Status : int
    {
        Active = 1,
        Inactive = 2
    }

    """, enumDecl.ToCsharpString());
var del = new DelegateDeclaration("MyDelegate");
del.ReturnType = typeof(void);
del.Arguments.Add(new MethodArgumentDeclaration(typeof(string), "message"));

Assert.Equal("""
    delegate void MyDelegate(string message);

    """, del.ToCsharpString());

Generic types

A TypeParameter added to Parameters declares a type parameter. It converts to a type reference, so it can be used as the type of a member:

var iface = new InterfaceDeclaration("IRepository");
var typeParameter = new TypeParameter("T");
iface.Parameters.Add(typeParameter);
iface.Members.Add(new MethodDeclaration("Get") { ReturnType = typeParameter });
var code = iface.ToCsharpString();

Assert.StartsWith("interface IRepository<T>", code);
Assert.Contains("Get();", code);

The Constraints of a type parameter accept the class, struct, unmanaged, new() and base type constraints (ClassTypeParameterConstraint, ValueTypeTypeParameterConstraint, UnmanagedTypeParameterConstraint, ConstructorParameterConstraint and BaseTypeParameterConstraint).

Members

ClassDeclares
FieldDeclarationA field, with an optional initializer.
PropertyDeclarationA property, with a Getter and a Setter.
MethodDeclarationA method, with its return type, arguments, type parameters and statements.
ConstructorDeclarationA constructor, with an optional base(...) or this(...) initializer.
EventFieldDeclarationAn event.
OperatorDeclarationAn operator, including implicit and explicit conversions.
EnumerationMemberA member of an enum.
var field = new FieldDeclaration("_count", typeof(int), Modifiers.Private, new LiteralExpression(0));

Assert.Equal("""
    private int _count = 0;

    """, field.ToCsharpString());
var method = new MethodDeclaration("Calculate");
method.Modifiers = Modifiers.Public;
method.ReturnType = typeof(int);
method.Arguments.Add(new MethodArgumentDeclaration(typeof(int), "value"));
method.Statements = new ReturnStatement(new LiteralExpression(42));

Assert.Equal("""
    public int Calculate(int value)
    {
        return 42;
    }

    """, method.ToCsharpString());
var cls = new ClassDeclaration("Person");
var ctor = cls.AddMember(new ConstructorDeclaration());
ctor.Modifiers = Modifiers.Public;
ctor.Arguments.Add(new MethodArgumentDeclaration(typeof(string), "name"));
ctor.Statements = new AssignStatement(new MemberReferenceExpression(new ThisExpression(), "_name"), new ArgumentReferenceExpression("name"));

Assert.Equal("""
    class Person
    {
        public Person(string name)
        {
            this._name = name;
        }
    }

    """, cls.ToCsharpString());

A property accessor is a PropertyAccessorDeclaration. A statement converts to an accessor, and an accessor without statements is written as an auto-implemented accessor (get;):

var prop = new PropertyDeclaration("Name", typeof(string));
prop.Modifiers = Modifiers.Public;
prop.Getter = new ReturnStatement(new MemberReferenceExpression(new ThisExpression(), "_name"));
prop.Setter = new AssignStatement(new MemberReferenceExpression(new ThisExpression(), "_name"), new ValueArgumentExpression());

Assert.Equal("""
    public string Name
    {
        get
        {
            return this._name;
        }
        set
        {
            this._name = value;
        }
    }

    """, prop.ToCsharpString());

Attributes

A CustomAttribute has a type and Arguments. A CustomAttributeArgument with a PropertyName is a named argument. Add attributes to the CustomAttributes collection of a type, a member, an argument or an accessor:

var attr = new CustomAttribute(typeof(ObsoleteAttribute));
attr.Arguments.Add(new CustomAttributeArgument(new LiteralExpression("Use NewMethod instead")));

Assert.Equal("""[global::System.ObsoleteAttribute("Use NewMethod instead")]""", attr.ToCsharpString());