UP | HOME

Support via Liberapay

org-babel Support for C# source blocks

Org Mode support for
C#

Introduction

Babel can compile and evaluate C# code blocks with this module. It is different from ob-csharp in org-contrib in that it allows for significantly more complex setups, which is outlined in the following. A C# code-block

  • will be compiled in the context of a csproj-file
  • it can have dependencies on
    • other projects (define in csproj-files)
    • compiled assemblies (*.dll-files)
    • nuget packages (custom nuget feeds can be configured)
  • can be compiled with arbitrary .NET SDKs
  • allows for arbitrary project settings

Requirements and Setup

A version of .NET should be installed on the host system that is executing the C# code blocks. Please make sure that the target framework configured in the variable org-babel-csharp-default-target-framework is available on your system. Currently, this variable is set to the most recent discoverable .NET SDK found on the host system. If that is not what you intend as a default, this variable can be configured e.g. like

(setf org-babel-csharp-default-target-framework "net8.0")

Org Mode Features for C#

Configuration

There are a few global variables that will have effect on every C# code block.

org-babel-csharp-compiler
sets the compiler that will be used to compile the code block's csproj file. Default is dotnet.
org-babel-csharp-default-target-framework
depending on the installed .NET SDKs (get a list by calling dotnet --list-sdks), this variable can be set as desired. It will be used as the default when no :framework header argument was given. If not configured, the most recent discoverable version will be used.
org-babel-csharp-additional-project-flags
if the default "minimal viable" csproj configuration does not fit your needs, you can add arbitrary additional configuration here. When not nil it will be put in the main PropertyGroup section of the csproj file. Note that it must be in "encoded" in a valid XML syntax as it will be used as-is. You can for example set the build configuration of the code-block to Release with (setq org-babel-csharp-additional-project-flags "<Configuration>Release</Configuration>")

You might want to reset some of these variables to their defaults in case you need different, or default, configuration in subsequent code blocks.

Header arguments

:main
defines whether or not to wrap the code block's content in a default void Main(string[] args) function. Anything other than "no" (including no :main argument at all) as a value for this argument will result in wrapping the code block in a Main function when expanding.
:nugetconfig
path to a custom NuGet.config file to use for the code block. This is useful if you have a local *.nupkg or a feed different from nuget.org and want to use it in your source block.
:framework
the target framework of the code block. See org-babel-csharp-default-target-framework for permanent configuration of this.
:class
defines the name of the program wrapper class. It accepts string arguments. If left empty, it will use the default name Program as a program wrapper class name. A value of "no" will inhibit a wrapper class.
:references
accepts a list of dependencies. Three types of references are allowed
  1. Project References can be passed using a full or relative path to an existing project. To set a project reference, the file path will need a .csproj file extension. Example: :references '( "/home/me/otherproject/theproject.csproj")
  2. Assembly Reference can be passed using a full or relative path to an existing assembly. To set an assembly reference, the file path will need a .dll file extension. Example: :references '( "/home/me/otherproject/theproject.dll")
  3. Nuget Package Reference can be passed by simply adding the correct name of the package to the reference list. In order for this to work, the org-babel-csharp-nuget-config variable should define a nuget feed from where this package can be fetched. To set a nuget reference, don't add a file extension to the dependency. In case you want a package with a specific version, pass the respective package as a cons cell where the cdr is a string representing the desired version. Example: :references '(Newtonsoft.Json) will give you any version of Newtonsoft.Json, :references '(("Newtonsoft.Json" . "13.0.3")) will give you version 13.0.3 of that package.
:usings
a list of namespaces to include in the using block of the resulting .cs file. This is
  • a convenience feature when you set :class "no" :main "no" (as you could type using project.a.featureb; at the start of the code block)
  • a necessity if the main function and the wrapper class are generated automatically and you need to pass in namespace dependencies (and don't want to write fully qualified names for usages of the respective external dependencies)
:cmdline
command line arguments that will be passed to the compiled executable of the respective source block.

Examples

Minimal Viable C# Code Block

Simply adding a new code block and setting its language to csharp is sufficient. The following will compile and evaluate (as an <OutputType>Exe</OutputType> project).

#+begin_src csharp
  Console.WriteLine("Hello from C#");
#+end_src
Hello from C#

Custom Class

By default, the code within the code block is wrapped in a class called Program. This name can be configured in with the :class header argument. Whether or not an automated Main function should be created can be configured in the :main header argument (everything else than "no" will result in an automatic main function). The generated code will be put in a temporary directory governed by org-babel-temporary-directory.

The following code block will be

  • wrapped in a class MyTest
  • completely wrapped in a static void Main(string[] args) function
#+begin_src csharp :class "MyClass" :usings '("System" "System.Diagnostics" "System.Reflection") :main yes
  string projectName = Assembly.GetCallingAssembly().GetName().Name;
  string projectDirectory = "my-test";
  var stackTrace = new StackTrace();
  var firstStackFrame = stackTrace.GetFrame(0);
  string methodName = firstStackFrame.GetMethod().ToString();
  string className = firstStackFrame.GetMethod().DeclaringType.ToString();

  Console.WriteLine($"Directory:\t{projectDirectory}");
  Console.WriteLine($"Project:\t{projectName}");
  Console.WriteLine($"Class:\t{className}");
  Console.WriteLine($"Method:\t{methodName}");
#+end_src
Dll Name: obcsRELp52.dll
Project: obcsRELp52
Class: obcsi7SYVW.MyClass
Method: Void Main(System.String[])

Input Variables

Idiomatic variable types are detected automatically. The following code block indicates the correct detection of

  • System.Int32
  • System.Double
  • System.String
#+begin_src csharp :var a=3 b="pizza" c=5.3 d=-1
  int myInt = 1;
  string PrintTypeVal<T>(T tp)
  {
      return $"{tp.GetType().ToString()} {tp}";
  }

  var myString = $"{PrintTypeVal(myInt)}\n{PrintTypeVal(a)}\n{PrintTypeVal(b)}\n{PrintTypeVal(c)}\n{PrintTypeVal(d)}";

  Console.WriteLine(myString);
#+end_src
System.Int32 1
System.Int32 3
System.String pizza
System.Double 5.3
System.Int32 -1

Output Formatting

In the above code blocks, tabular output was used implicitly. List output is supported as well but must be specified like so

#+begin_src csharp :results raw list
  Console.WriteLine("Item 1");
  Console.WriteLine("Item 2");
  Console.WriteLine("Item 3");
  Console.WriteLine("Item 4");
#+end_src
  • Item 1
  • Item 2
  • Item 3
  • Item 4

NuGet References

With the following code block, we create a string serialization based on the Newtonsoft.Json NuGet package. Since this is available from nuget.org, no additional configuration is needed.

#+begin_src csharp :references '(("Newtonsoft.Json" . "13.0.3")) :usings '("System" "Newtonsoft.Json") :main no :project "json-test" :results raw
  public class DTO
  {
      public int TheInt { get; set; }
      public string TheString { get; set; }
  }

  static void Main(string[] args)
  {
      DTO myDto = new() { TheInt = 12, TheString = "ok" };

      string json = JsonConvert.SerializeObject(myDto, Formatting.Indented);
      Console.WriteLine($"{json}");
  }
#+end_src

{ "TheInt": 12, "TheString": "ok" }

Documentation from the orgmode.org/worg/ website (either in its HTML format or in its Org format) is licensed under the GNU Free Documentation License version 1.3 or later. The code examples and css stylesheets are licensed under the GNU General Public License v3 or later.