Programming in D for CSharp Programmers
Contents
Introduction: Hello world
The C# Way
using System; class Hello { static void Main(string[] args) { Console.WriteLine("Hello world!"); } }
The D Way
import std.stdio; void main(string[] args) { writeln("Hello world"); }
Things we learnt so far:
- standard file extension for D source code files is
.d
- we
import
a module instead ofusing
a namespace; - static methods can be declared outside a class;
- we can call directly any method even if it’s not declared in a class (
writeln
); writeln
is D equivalent for C#Console.WriteLine
;- syntax is exactly the same as in C# (method definitions, string qualifiers, array declarations, comments)
- many of the keywords are exactly the same (
void
,string
);
Coding styles
- D programmers prefer to use the camelCase notation instead of PascalCase for method names, variable names and enum members;
- Module names (C# namespaces) are always in lowercase due to cross-platform compatibility regarding file names.
- If there are conflicts between a named entity and a keyword, in C# you can use verbatim identifiers (@while). D does not have verbatim identifiers, but the convention is to add an underscore at the end of the entity (while_).
The C# Way
- classes, structs, delegate types: PascalCase
- methods, properties, events, enum declaration: PascalCase
- local variables, fields: camelCase
- constants, enum members: PascalCase
The D Way
- classes, structs, delegate types: PascalCase
- methods, properties, enum declaration: PascalCase
- local variables, fields: camelCase
- constants, enum values: camelCase
Type system
Built-in types
Basic type names are very similar in both languages with the following differences:
- The 8 bit signed integer from C#
sbyte
is written in D asbyte
; - The 8 bit unsigned integer from C#
byte
is written in D asubyte
; - There is no type equivalence for
decimal
- There are three types of char in D:
char
,wchar
anddchar
, each of them corresponding to an UTF encoding : UTF-8, UTF-16 and UTF-32. Since C# is using internally UTF-16 chars, the direct equivalent of C#char
is Dwchar
. - There are also three types of string in D:
string
,wstring
anddstring
. The direct equivalent of C#string
is in fact Dwstring
. These are not keywords in D, they are in fact declared as aliases to immutable char arrays. - There is another floating point type in D:
real
with no type equivalence in C#. - Complex floating point types
Complex<T>
are keywords in D:cfloat, cdouble, creal
and they imaginary counterparts areifloat
,idouble
,ireal
;
Arrays
Arrays in D are not too different than the ones form C# (for D see D Koans and dlang)
Static array
Initialize with a literal
The C# Way
string[] fruits = {"banana", "mango", "apple", "orange"}; // or long mode //string[] fruits = new string[4]{"banana", "mango", "apple", "orange"}; Assert.AreEqual(fruits[0], "banana"); Assert.AreEqual(fruits.Length, 4);
The D Way
string[4] fruits = ["banana", "mango", "apple", "orange"]; assertEquals(fruits[0], "banana"); assertEquals(fruits.length, 4);
Initialize with same value
The C# Way
// no short way int[] b = { 1, 1, 1}; // 3 elements with same value 1
The D Way
int[3] b = 1; // 3 elements with same value 1
Dynamic array
The C# Way
List<T> works very similarly to a dynamic array
using System.Collections.Generic; ... List<string> fruits = new List<string>{"banana", "mango"}; Assert.AreEqual(fruits.Count, 2);
fruits.Add("strawberry"); Assert.AreEqual(fruits.Count, 3); Assert.AreEqual(fruits[2], "strawberry");
The D Way
string[] fruits = ["banana", "mango"]; assertEquals(fruits.length, 2); fruits ~= "strawberry"; assertEquals(fruits.length, 3); assertEquals(fruits[2], "strawberry");
Pointers
Since D is not a managed language, you are free to use pointers anywhere in the code, without encompassing them in an unsafe context. On the contrary, D code is by default unsafe, but you can force the safe context using the @safe
keyword:
The C# Way
int value; // here you can't use pointers unsafe { int* p = &value }
The D Way
int value; int* p = &value @safe { //here you can't use pointers }
Delegates
Delegates in D are declared with the same keyword, but the return type precedes the declaration:
The C# Way
delegate int F
The D Way
int delegate(int x) Foo; int function(int x) Foo;
Since D doesn't need to declare methods inside a class, you can declare also a function, equivalent to a delegate without class context. A notable difference between C# and D is the fact that delegates are not multicast in D, therefore you cannot join or remove them.
Enums
There is no difference between enum declarations, except that so called C# flags enums are not necessarely decorated with the [Flags] attribute:
The C# Way
enum Option { Option1, Option2 } [Flags] enum Options { Option1, Option2, All = Option1 | Option2 }
The D Way
The members of enums should be camelCased, so their first letter is lowercase. (see D Style
enum Color { red, green, blue }
enum Permission { read, write, all = read | wite }
Struct
Struct are declared exactly the same way in D except:
- There is no explicit layout in D. Nevertheless, there is a solution in the standard library.
- Structs cannot implement interfaces
The C# Way
public struct TimeOfDay { public int hour; public int minute; public TimeOfDay(int h, int m) { hour = h; minute = m; } } ... var t1 = new TimeOfDay(8, 30); Assert.AreEqual(t1.minute, 30); // Declare a struct object without "new. TimeOfDay t2; t2.minute = 10; Assert.AreEqual(t2.minute, 10);
The D Way
struct TimeOfDay { int hour; int minute; } ... auto t1 = TimeOfDay(8, 30); // preferred syntax assertEquals(t1.minute, 30); TimeOfDay t2 = {9, 45}; // alternate C syntax assertEquals(t2.hour, 9); auto t3 = TimeOfDay(10); // not all members need to be specified assertEquals(t3.minute, 0);
Union
D has unions, the equivalent of a C# struct with explicit layout where all fields offsets are 0
The C# Way
[StructLayout(LayoutKind.Explicit)] struct MyUnion { [FieldOffset(0)] int someInt; [FieldOffset(0)] float someFloat; }
The D Way
union MyUnion { int someInt; float someFloat; }
Interfaces
Interfaces are declared in the same way as in C#, the only difference being that interfaces in D can have final methods, equivalent to C# abstract class
The C# Way
interface I { void Method(); } abstract class C { void Method() { Console.WriteLine("hello"); } }
The D Way
interface I { void Method(); } interface C { final void Method() { Console.WriteLine("hello"); } }
Generic Types
D is not using generics, instead it has a more powerful concept named templates.
The C# Way
public class A { public void Foo<T>(T arg) {} } public class C<T> where T: class { } public class X { } public class D<T> where T: X {} ... A a = new A(); a.Foo<int>(1); C<A> c = new C<A>(); // C<int> i = new C<int>(); compiler error: int isn't a class D<X> d = new D<X>(); //D<A> d = new D<A>();compiler error: A isn't X
The D Way
void Foo(T)(T arg); class C(T) if is(T == class) {} class X {} class D(T) if is(T : A) {}
There are no direct equivalents of other generic constraints, but the D template system is so versatile that you can create your own.
The C# Way
class C<T> where T: new() {}
The D Way
class C(T) if (is(typeof(new T()) == T))
The template constraint will be read as if the result of expresssion new T() is of type T. If the T class has no contructor or is some other type, the new T() expression will result in an error or some other type, therefore the constraint will not be satisfied.