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.
Events
There is no such concept in D language, but D programmers prefer to use signals and slots instead of events.
If you are keen to use events in D, a quick and dirty way to implement them can be found below:
TO DO
Dynamic Types
There is no such concept in D language. All types must be known at compile time but the same semantics can be simulated by forwarding.
Boxing
TO DO u
Nullable types
'TO DO
Enumerable types
'TO DO
Code Attributes
Property
The C# Way
class Person { public Person() { Name = "Default Name"; } public string Name { get; set; } }
The D Way
class Person { private string _name; this() { _name = "Default Name"; } @property string name() { return _name; } @property void name(string n) { _name = n; } }