Difference between revisions of "Programming in D for CSharp Programmers"

From D Wiki
Jump to: navigation, search
(Property)
(Undo revision 10004 by Ceydacoder (talk))
(Tag: Undo)
 
(88 intermediate revisions by 8 users not shown)
Line 3: Line 3:
 
''The C# Way''
 
''The C# Way''
  
using System;
+
file: hello.cs
class Hello {
+
<syntaxhighlight lang="C#">
  static void Main(string[] args) {
+
using System;
    Console.WriteLine(&quot;Hello world!&quot;);
+
class Hello
  }
+
{
}
+
    static void Main(string[] args)
 +
    {
 +
        Console.WriteLine("Hello world!");
 +
    }
 +
}
 +
</syntaxhighlight>
  
 
''The D Way''
 
''The D Way''
  
import std.stdio;
+
file: hello.d
void main(string[] args) {
+
<syntaxhighlight lang="D">
  writeln(&quot;Hello world&quot;);
+
import std.stdio;
  }
+
void main(string[] args)
 +
{
 +
    writeln("Hello world!");
 +
}
 +
</syntaxhighlight>
 +
 
 +
Things we have learnt so far:
 +
 
 +
* standard file extension for D source code files is <code>.d</code>
 +
* we <code>import</code> a module instead of <code>using</code> a namespace;
 +
* methods (functions) can be top-level, that is, not declared inside a class; and called without being prefixed with an object/class or class name (e.g. <code>writeln</code>)
 +
* <code>writeln</code> is the D equivalent for C#'s <code>System.Console.WriteLine</code>;
 +
* some syntax is exactly the same as in C# (method definitions, string qualifiers, array declarations, comments)
 +
* many of the keywords are exactly the same (<code>void,string</code>);
 +
 
 +
= Documentation Comments =
 +
''The C# Way''
 +
<syntaxhighlight lang="C#">
 +
/// <summary>
 +
/// Documentation comment
 +
/// </summary>
 +
/// <param name="foo">...</param>
 +
/// <param name="bar">...</param>
 +
  /// <returns>...</returns>
 +
</syntaxhighlight>
 +
 
 +
''The D Way''
 +
<syntaxhighlight lang="D">
 +
/**
 +
  Documentation comment
 +
 +
  Params:  foo = param description
 +
          bar = param description
 +
 +
  Returns: return value
 +
 +
  Throws:  Exception
 +
*/
 +
</syntaxhighlight>
  
Things we learnt so far:
+
= Compilation =
 +
== Simple compilation ==
  
* standard file extension for D source code files is <code>.d</code></li>
+
''The C# Way''
* we <code>import</code> a module instead of <code>using</code> a namespace;</li>
 
* static methods can be declared outside a class;</li>
 
* we can call directly any method even if it’s not declared in a class (<code>writeln</code>);</li>
 
* <code>writeln</code> is D equivalent for C# <code>Console.WriteLine</code>;</li>
 
* syntax is exactly the same as in C# (method definitions, string qualifiers, array declarations, comments)
 
* many of the keywords are exactly the same (<code>void</code>, <code>string</code>);
 
  
 +
With [http://www.mono-project.com/docs/about-mono/languages/csharp/ mcs] mono compiler:
 +
$ mcs source1.cs source2.cs -out:program.exe
  
= Coding styles =
+
''The D Way''
  
* D programmers prefer to use the camelCase notation instead of PascalCase for method names, variable names and enum members;
+
With [[DMD]] Digital Mars compiler:
* Module names (C# namespaces) are always in lowercase due to cross-platform compatibility regarding file names.
+
$ dmd source1.d source2.d -ofprogram
* 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_).
 
  
 +
== Conditional Compilation Directives ==
 
''The C# Way''
 
''The C# Way''
  
* classes, structs, delegate types: PascalCase
+
Defines one or more conditional symbols (short: -d)
* methods, properties, events, enum declaration: PascalCase
 
* local variables, fields: camelCase
 
* constants, enum members: PascalCase
 
  
 +
-define:S1[;S2]
 +
<syntaxhighlight lang="C#">
 +
// compile with: /define:xx
 +
// or uncomment the next line:
 +
//#define xx
 +
 +
[Test] public void define_test() {
 +
    int x = 0;
 +
    #if (xx)
 +
      x = 5;
 +
    #else
 +
      x = 1;
 +
    #endif
 +
    Assert.AreEqual(x, 5);
 +
}
 +
</syntaxhighlight>
 
''The D Way''
 
''The D Way''
  
* classes, structs, delegate types: PascalCase
+
-version=S1
* methods, properties, enum declaration: PascalCase
+
<syntaxhighlight lang="D">
* local variables, fields: camelCase
+
// compile with: -version=xx
* constants, enum values: camelCase
+
// or uncomment the next line:
 +
//version = xx
 +
 +
unittest {
 +
    int x = 0;
 +
    version(xx) {
 +
      x = 5;
 +
    }
 +
    else {
 +
      x = 1;
 +
    }
 +
    assert( x == 5 );
 +
}
 +
</syntaxhighlight>
 +
 
 +
= 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_).
  
 +
 +
{| class="wikitable"
 +
! Type
 +
! C# Way
 +
! D Way
 +
|-
 +
|Class
 +
|PascalCase
 +
|PascalCase
 +
|-
 +
|Struct
 +
|PascalCase
 +
|PascalCase
 +
|-
 +
|class method
 +
|PascalCase
 +
|camelCase
 +
|-
 +
|class property
 +
|PascalCase
 +
|camelCase
 +
|-
 +
|enum declaration
 +
|PascalCase
 +
|PascalCase
 +
|-
 +
|enum value
 +
|PascalCase
 +
|camelCase
 +
|-
 +
|const
 +
|UPPER_CASE
 +
| -
 +
|-
 +
|local variable
 +
|camelCase
 +
|camelCase
 +
|}
  
 
= Type system =
 
= Type system =
Line 61: Line 171:
 
* There is another floating point type in D: <code>real</code> with no type equivalence in C#.
 
* There is another floating point type in D: <code>real</code> with no type equivalence in C#.
 
* Complex floating point types <code>Complex&lt;T&gt;</code> are keywords in D: <code>cfloat, cdouble, creal</code> and they imaginary counterparts are <code>ifloat</code>, <code>idouble</code>, <code>ireal</code>;
 
* Complex floating point types <code>Complex&lt;T&gt;</code> are keywords in D: <code>cfloat, cdouble, creal</code> and they imaginary counterparts are <code>ifloat</code>, <code>idouble</code>, <code>ireal</code>;
 +
 +
== Literals ==
 +
'''TO DO'''
  
 
== Arrays ==
 
== Arrays ==
  
Arrays in D are not too different than the ones form C# (for D see [https://github.com/ilmanzo/DLangKoans/blob/master/source/about_arrays.d D Koans] and [http://dlang.org/arrays.html#static-arrays dlang])
+
Arrays in D are not too different than the ones form C# (for D see [https://github.com/ilmanzo/DLangKoans/blob/master/source/07_about_arrays.d D Koans] and [http://dlang.org/arrays.html#static-arrays dlang])
  
 
=== Static array ===
 
=== Static array ===
 
==== Initialize with a literal ====
 
==== Initialize with a literal ====
 
''The C# Way''
 
''The C# Way''
  string[] fruits = {"banana", "mango", "apple", "orange"};
+
<syntaxhighlight lang="C#">
  // or long mode
+
string[] fruits = {"banana", "mango", "apple", "orange"};
  //string[] fruits = new string[4]{"banana", "mango", "apple", "orange"};
+
// or long mode
  Assert.AreEqual(fruits[0], "banana");
+
string[] longFruits = new string[4]{"banana", "mango", "apple", "orange"};
  Assert.AreEqual(fruits.Length, 4);
+
Assert.AreEqual(fruits[0], "banana");
 +
Assert.AreEqual(fruits.Length, 4);
 +
</syntaxhighlight>
  
 
''The D Way''
 
''The D Way''
string[4] fruits = ["banana", "mango", "apple", "orange"];
+
<syntaxhighlight lang="D">
assertEquals(fruits[0], "banana");
+
string[4] fruits = ["banana", "mango", "apple", "orange"];
assertEquals(fruits.length, 4);
+
assertEquals(fruits[0], "banana");
 +
assertEquals(fruits.length, 4);
 +
</syntaxhighlight>
  
 
==== Initialize with same value ====
 
==== Initialize with same value ====
 
''The C# Way''
 
''The C# Way''
// no short way
 
int[] b = { 1, 1, 1}; // 3 elements with same value 1
 
  
 +
<syntaxhighlight lang="C#">
 +
// no short way
 +
int[] b = { 1, 1, 1}; // 3 elements with same value 1
 +
</syntaxhighlight>
 
''The D Way''
 
''The D Way''
int[3] b = 1; // 3 elements with same value 1
+
<syntaxhighlight lang="D">
 +
int[3] b = 1; // 3 elements with same value 1
 +
</syntaxhighlight>
  
 
=== Dynamic array ===
 
=== Dynamic array ===
For D see [https://github.com/ilmanzo/DLangKoans/blob/master/source/about_arrays.d D Koans] and [http://dlang.org/arrays.html#static-arrays dlang]
+
For D see [https://github.com/ilmanzo/DLangKoans/blob/master/source/07_about_arrays.d D Koans] and [http://dlang.org/arrays.html#static-arrays dlang]
  
 
''The C# Way''
 
''The C# Way''
  
List<T> works very similarly to a dynamic array
+
List<T> behaves similarly to a dynamic array
using System.Collections.Generic;
+
<syntaxhighlight lang="C#">
...
+
using System.Collections.Generic;
List<string> fruits = new List<string>{"banana", "mango"};
+
...
Assert.AreEqual(fruits.Count, 2);
+
List<string> fruits = new List<string>{"banana", "mango"};
 
+
Assert.AreEqual(fruits.Count, 2);
fruits.Add("strawberry");
+
Assert.AreEqual(fruits.Count, 3);
+
fruits.Add("strawberry");
Assert.AreEqual(fruits[2], "strawberry");
+
Assert.AreEqual(fruits.Count, 3);
 
+
Assert.AreEqual(fruits[2], "strawberry");
 +
</syntaxhighlight>
  
 
''The D Way''
 
''The D Way''
string[] fruits = ["banana", "mango"];
+
<syntaxhighlight lang="D">
assertEquals(fruits.length, 2);
+
string[] fruits = ["banana", "mango"];
 +
assertEquals(fruits.length, 2);
 
   
 
   
fruits ~= "strawberry";
+
fruits ~= "strawberry";
assertEquals(fruits.length, 3);
+
assertEquals(fruits.length, 3);
assertEquals(fruits[2], "strawberry");
+
assertEquals(fruits[2], "strawberry");
 +
</syntaxhighlight>
  
 
== Pointers ==
 
== Pointers ==
Line 116: Line 240:
  
 
''The C# Way''
 
''The C# Way''
int value;
+
<syntaxhighlight lang="C#">
// here you can't use pointers
+
int value;
unsafe {
+
// here you can't use pointers
   int* p = &amp;value
+
unsafe {
}
+
   int* p = &value;
 
+
}
 +
</syntaxhighlight>
 
''The D Way''
 
''The D Way''
 
+
<syntaxhighlight lang="D">
int value;
+
int value;
int* p = &amp;value
+
int* p = &value;
@safe {
+
@safe {
 
   //here you can't use pointers
 
   //here you can't use pointers
}
+
}
 +
</syntaxhighlight>
  
 
== Delegates ==
 
== Delegates ==
Line 135: Line 261:
  
 
''The C# Way''
 
''The C# Way''
delegate int F
+
<syntaxhighlight lang="C#">
 
+
delegate int F
 +
</syntaxhighlight>
 
''The D Way''
 
''The D Way''
int delegate(int x) Foo;
+
<syntaxhighlight lang="D">
int function(int x) Foo;
+
int delegate(int x) Foo;
 
+
int function(int x) Foo;
 +
</syntaxhighlight>
 
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.
 
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.
  
Line 148: Line 276:
  
 
''The C# Way''
 
''The C# Way''
enum Option {  
+
<syntaxhighlight lang="C#">
 +
enum Option  
 +
{  
 
     Option1,  
 
     Option1,  
 
     Option2
 
     Option2
}
+
}
  [Flags]
+
   
enum Options {
+
[Flags]
     Option1,
+
enum Permission
     Option2,
+
{
     All = Option1 | Option2
+
     read,
}
+
     write,
 +
     all = read | wite
 +
}
 +
</syntaxhighlight>
  
 
''The D Way''
 
''The D Way''
  
 
The members of enums should be camelCased, so their first letter is lowercase. (see [http://dlang.org/dstyle.html D Style]
 
The members of enums should be camelCased, so their first letter is lowercase. (see [http://dlang.org/dstyle.html D Style]
enum Color {   
+
 
 +
<syntaxhighlight lang="C#">
 +
enum Color
 +
{   
 
   red,  
 
   red,  
 
   green,  
 
   green,  
 
   blue
 
   blue
}
+
}
 
+
enum Permission {
+
enum Permission
 +
{
 
     read,
 
     read,
 
     write,
 
     write,
 
     all = read | wite
 
     all = read | wite
}
+
}
 +
</syntaxhighlight>
  
 
== Struct ==
 
== Struct ==
Line 181: Line 319:
  
 
''The C# Way''
 
''The C# Way''
public struct TimeOfDay {
+
<syntaxhighlight lang="C#">
 +
public struct TimeOfDay {
 
   public int hour;
 
   public int hour;
 
   public int minute;
 
   public int minute;
Line 188: Line 327:
 
     minute = m;
 
     minute = m;
 
   }
 
   }
}
+
}
...
+
...
 +
 
 +
var t1 = new TimeOfDay(8, 30);
 +
Assert.AreEqual(t1.minute, 30);
 
   
 
   
var t1 = new TimeOfDay(8, 30);
+
// Declare a struct object without "new"
Assert.AreEqual(t1.minute, 30);
+
TimeOfDay t2;
+
t2.minute = 10;
// Declare a struct object without "new.
+
Assert.AreEqual(t2.minute, 10);
TimeOfDay t2;
+
</syntaxhighlight>
t2.minute = 10;
 
Assert.AreEqual(t2.minute, 10);
 
 
 
 
''The D Way''
 
''The D Way''
struct TimeOfDay {
+
<syntaxhighlight lang="D">
 +
struct TimeOfDay {
 
   int hour;
 
   int hour;
 
   int minute;
 
   int minute;
  }
+
}
  ...  
+
...
 +
   
 +
// preferred syntax
 +
auto t1 = TimeOfDay(8, 30);  
 +
assert(t1.hour == 8);
 +
assert(t1.minute == 30);
 +
 +
// alternate C syntax, deprecated
 +
TimeOfDay t2 = {9, 45};     
 +
assert(t2.hour == 9);
 
   
 
   
auto t1 = TimeOfDay(8, 30); // preferred syntax
+
// with designated initializers
assertEquals(t1.minute, 30);
+
auto t3 = {minute: 20, hour: 8};
TimeOfDay t2 = {9, 45};      // alternate C syntax
+
assert(t3.hour == 8);
assertEquals(t2.hour, 9);
+
assert(t3.minute == 20);
auto t3 = TimeOfDay(10);    // not all members need to be specified
+
 
assertEquals(t3.minute, 0);
+
auto t4 = TimeOfDay(10);    // not all members need to be specified
 
+
assert(t4.hour == 10);
 +
assert(t4.minute == 0);
 +
</syntaxhighlight>
  
 
== Union ==
 
== Union ==
Line 218: Line 369:
  
 
''The C# Way''
 
''The C# Way''
 
+
<syntaxhighlight lang="C#">
[StructLayout(LayoutKind.Explicit)]
+
[StructLayout(LayoutKind.Explicit)]
struct MyUnion {
+
struct MyUnion {
 
   [FieldOffset(0)]
 
   [FieldOffset(0)]
 
   int someInt;
 
   int someInt;
 
   [FieldOffset(0)]
 
   [FieldOffset(0)]
 
   float someFloat;
 
   float someFloat;
}
+
}
 
+
</syntaxhighlight>
 
''The D Way''
 
''The D Way''
 
+
<syntaxhighlight lang="D">
union MyUnion {
+
union MyUnion {
 
   int someInt;
 
   int someInt;
 
   float someFloat;
 
   float someFloat;
}
+
}
 +
</syntaxhighlight>
  
 +
== Interfaces ==
  
== 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 a C# abstract class
  
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''
 +
<syntaxhighlight lang="C#">
 +
interface I
 +
{
 +
    void Method();
 +
}
 +
 +
abstract class C
 +
{
 +
    void Method()
 +
    {
 +
      Console.WriteLine("hello");
 +
    }
 +
}
 +
</syntaxhighlight>
 +
''The D Way''
 +
<syntaxhighlight lang="D">
 +
interface I
 +
{
 +
    void method();
 +
}
 +
 +
interface C
 +
{
 +
    final void method()
 +
    {
 +
        writeln("hello");
 +
    }
 +
}
 +
</syntaxhighlight>
  
 +
== Classes ==
 
''The C# Way''
 
''The C# Way''
 +
<syntaxhighlight lang="C#">
 +
class A {
 +
    private int myValue;  // not accessible from outside
 +
                                                                                   
 +
    public A(int startValue) { // constructor
 +
      myValue = startValue;
 +
    }
 +
    public int Value {
 +
      get { return myValue; }
 +
    }
 +
                                                                                   
 +
}
 +
                                                                                   
 +
class B: A {
 +
    public B(): base(3) {}
 +
                                                                                   
 +
    public int GetDoubleValue() {
 +
      //return myValue * 2;  // Error: myValue is inaccessible due to its protection level
 +
      return this.Value * 2;
 +
    }
 +
}
 +
                                                                                   
 +
[TestFixture] public class AboutClasses {
 +
    [Test]public void inheritance() {
 +
      B instance = new B();
 +
      Assert.AreEqual(instance.GetDoubleValue(), 6);
 +
    }
 +
}
 +
</syntaxhighlight>
  
interface I {
+
''The D Way''
   void Method();
+
<syntaxhighlight lang="D">
  }
+
class A {
 +
   private int myValue// not accessible from outside
 
   
 
   
abstract class C {
+
   this(int startValue) { // constructor
   void Method() {
+
     myValue = startValue;
     Console.WriteLine("hello");
 
 
   }
 
   }
}
+
}
 
+
''The D Way''
+
class B: A {
interface I {
+
  this() {
  void Method();
+
    super(3);   // call constructor on parent class A
}
+
  }  
 
   
 
   
interface C {
+
   auto getDoubleValue() {
   final void Method() {
+
    return myValue * 2; // B doesn't have this field but can access the value
      Console.WriteLine("hello");
 
 
   }
 
   }
  }
+
}
 +
   
 +
class AboutClasses {
 +
 
 +
  @Test public void inheritance() {
 +
      auto instance = new B;
 +
      assertEquals(instance.getDoubleValue(), 6);
 +
    }
 +
}
 +
</syntaxhighlight>
  
 
== Generic Types ==
 
== Generic Types ==
Line 267: Line 487:
  
 
''The C# Way''
 
''The C# Way''
 +
<syntaxhighlight lang="C#">
 +
public class A {
 +
  public void Foo<T>(T arg) {}
 +
}
 +
// use
 +
A a = new A();
 +
a.Foo<int>(1);
 +
 +
 +
public class C<T> where T: class { }
 +
...                                         
 +
C<A> c = new C<A>();
 +
// C<int> i = new C<int>(); compiler error: int isn't a class
 +
 +
 +
public class X { }
 +
public class D<T> where T: X {}
 +
 +
D<X> d = new D<X>();
 +
//D<A> d = new D<A>();compiler error: A isn't X
 +
</syntaxhighlight>
  
public class A {
+
''The D Way''
  public void Foo<T>(T arg) {}
+
<syntaxhighlight lang="D">
}
+
void foo(T)(T arg) {}
  public class C<T> where T: class { }
+
// use
public class X { }
+
foo!int(3);
public class D<T> where T: X {}
+
foo!string("bar");
  ...                                         
+
   
A a = new A();
+
class X {}  
a.Foo<int>(1);
+
class C(T) if is(T == class) {}
C<A> c = new C<A>();
+
   
// C<int> i = new C<int>(); compiler error: int isn't a class
+
// use
 +
C c = new C!X;
 +
// C c = new C!int; compiler error: int isn't a class
 +
 
   
 
   
D<X> d = new D<X>();
+
class D(T) if is(T : X) {}
//D<A> d = new D<A>();compiler error: A isn't X
+
auto d = new D!X;
 +
//auto d = new D!(A);compiler error: A isn't X
 +
</syntaxhighlight>
 +
=== Constraints ===
 +
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''
 +
<syntaxhighlight lang="C#">
 +
class C<T> where T: new() {}
 +
</syntaxhighlight>
 +
''The D Way''
 +
<syntaxhighlight lang="D">
 +
class C(T) if (is(typeof(new T()) == T))
 +
</syntaxhighlight>
 +
The template constraint will be read as if the result of expression <code>new T()</code> is of type T. If the T class has no constructor or is some other type, the <code>new T()</code> 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 [http://dlang.org/phobos/std_signals.html 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 (Credit: https://forum.dlang.org/post/dcdtuqyrxpteuaxmvwft@forum.dlang.org):
  
 +
<syntaxhighlight lang="D">
 +
class Event(T...)
 +
{
 +
    alias CB = void delegate(T);
 +
    CB[] callbacks;
  
''The D Way''
+
    // NOTE: This implementation uses the `+` operator as it is likely more
 +
    // familiar to C# programmers, but a more idiomatic D operator would be
 +
    // `~`, D's concatenation operator.
  
void Foo(T)(T arg);
+
    void opOpAssign(string op)(CB handler)
class C(T) if is(T == class) {}
+
        if (op == "+" || op == "-")
class X {}
+
    {
class D(T) if is(T : A) {}
+
        static if (op == "+")
 +
            callbacks ~= handler;
 +
        else
 +
        {
 +
            import std.algorithm.mutation : remove;
 +
            callbacks = callbacks.remove!(x => x == handler);
 +
        }
 +
    }
  
There are no direct equivalents of other generic constraints, but the D template system is so versatile that you can create your own.
+
    void opOpAssign(string op)(void function(T) handler)
 +
        if (op == "+" || op == "-")
 +
    {
 +
        import std.functional : toDelegate;
 +
        opOpAssign!op(toDelegate(handler));
 +
    }
  
''The C# Way''
+
    void opCall(T args)
 +
    {
 +
        foreach (cb; callbacks)
 +
            cb(args);
 +
    }
 +
}
  
class C<T> where T: new() {}
+
class Property(T)
 +
{
 +
    private T _value;
  
''The D Way''
+
    alias event = Event!(T, T);
  
class C(T) if (is(typeof(new T()) == T))
+
    auto changing = new event();
 +
    auto changed = new event();
  
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.
+
    T value() @property
 +
    {
 +
        return _value;
 +
    }
  
 +
    T value(T newValue) @property
 +
    {
 +
        if (_value != newValue)
 +
        {
 +
            T oldValue = _value;
 +
            changing(_value, newValue);
 +
            _value = newValue;
 +
            changed(oldValue, _value);
 +
        }
 +
        return _value;
 +
    }
 +
}
  
== Events ==
+
void main()
 +
{
 +
    auto test = new Property!(int)();
  
There is no such concept in D language, but D programmers prefer to use [http://dlang.org/phobos/std_signals.html signals and slots] instead of events.  
+
    int changingCount;
 +
    void onChanging(int oldValue, int newValue)
 +
    {
 +
        changingCount++;
 +
    }
 +
    test.changing += &onChanging;
  
If you are keen to use events in D, a quick and dirty way to implement them can be found below:
+
    int changedCount;
 +
    void onChanged(int oldValue, int newValue)
 +
    {
 +
        changedCount++;
 +
    }
 +
    test.changed += &onChanged;
 +
   
 +
    test.value = 1;  // value changed from 0 to 1
 +
    test.value = 1;  // value not changed
 +
    test.value = 2;  // value changed from 1 to 2
  
'''TO DO'''
+
    assert(changingCount == 2);
 +
    assert(changedCount == 2);
 +
}
 +
</syntaxhighlight>
  
 
== Dynamic Types ==
 
== Dynamic Types ==
Line 318: Line 642:
  
 
== Boxing ==
 
== Boxing ==
'''TO DO'''
+
 
u
+
<cite>Boxing, otherwise known as wrapping, is the process of placing a primitive type within an object so that the primitive can be used as a reference object</cite> ([https://en.wikipedia.org/wiki/Object_type_%28object-oriented_programming%29#Boxing wikipedia])
 +
 
 +
Value types in D are not boxed or unboxed automatically and do not inherit from ValueType. Also, you cannot implement interfaces for value types.
 +
Depending on your purpose, there are alternatives to boxing in D:
 +
 
 +
* if you just want to have a container for several types you can use [http://dlang.org/phobos/std_variant.html std.variant]
 +
* if you want to write general purpose method with object parameters, use generics
 +
 
 +
''The C# Way''
 +
<syntaxhighlight lang="D">
 +
[Test]public void boxing_test() {
 +
    int a = 1;
 +
    float b = 19.64f;
 +
                                     
 +
    object o = a;
 +
    Assert.AreEqual(o, 1);
 +
    Assert.AreEqual(tos(o), "1");
 +
                                     
 +
    o = b;
 +
    Assert.AreEqual(o, 19.64f);
 +
                                     
 +
    b = (float)o;
 +
    Assert.AreEqual(b, 19.64f);
 +
    Assert.AreEqual(tos(o), "19.64");
 +
}
 +
private string tos(object obj) {
 +
    return obj.ToString();
 +
}
 +
</syntaxhighlight>
 +
 
 +
''The D Way''
 +
<syntaxhighlight lang="D">
 +
import std.variant;
 +
 
 +
@Test void boxing_test() {
 +
    int a = 1;
 +
    float b = 19.64f;
 +
 +
    Variant o = a;
 +
    assert(o.type == typeid(int));
 +
    assert(tos(o) == "1");
 +
                                 
 +
    v = b;
 +
    auto x = v.get!(float);
 +
    assert(x == 19.64f);
 +
}
 +
                                 
 +
string tos(T)(T obj) {
 +
    return to!string(obj);
 +
}
 +
</syntaxhighlight>
 +
 
 
== Nullable types ==
 
== Nullable types ==
'''TO DO''
+
Value types are not nullable in the D language.
 +
But there is a solution in the standard library module [[typecons]] - templated struct [[Nullable]] - to simulate this behavior. This is the same solution as in C#, although C# provides the shorthand syntax of "Type? variable-name", which can hide the usage of templated struct System.Nullable<>.
 +
 
 +
''[https://msdn.microsoft.com/en-us/library/vstudio/1t3y8s4s%28v=vs.110%29.aspx The C# Way]''
 +
<syntaxhighlight lang="C#">
 +
int? a = null; // short hand for: System.Nullable<int> a = null;
 +
// attempting to use a.Value will cause an exception to be thrown
 +
int? num = 10;
 +
if (num.HasValue) {
 +
    System.Console.WriteLine(3 * num.Value); // prints 30
 +
} else {
 +
    System.Console.WriteLine("num = Null");
 +
}
 +
</syntaxhighlight>
 +
 
 +
''[http://dlang.org/phobos/std_typecons.html#.Nullable The D Way]''
 +
 
 +
<syntaxhighlight lang="D">
 +
import std.typecons;
 +
 
 +
Nullable!int a; // a is null and cannot be used as an int until assigned a value
 +
                // attempting to use a as an int will cause an exception to be thrown
 +
Nullable!int num = 10;
 +
if ( ! num.isNull) {
 +
    writeln(num.get * 3);  // prints 30
 +
    writeln(num * 3);      // prints 30 - get not required (see below)
 +
} else {
 +
    writeln("num = Null");
 +
}
 +
int y = num;  // get property not required due to "alias get this" inside Nullable for
 +
              // automatic compiler conversion to int when Nullable!(int) is not appropriate
 +
int z = num.isNull ? int.init : num;
 +
num.nullify();    // num set to null - cannot be used as an int until re-assigned
 +
</syntaxhighlight>
  
 
== Enumerable types ==
 
== Enumerable types ==
'''TO DO''
+
In C#, a type can be considered enumerable if implements the [https://msdn.microsoft.com/en-us/library/system.collections.ienumerable.aspx IEnumerable] interface.
 +
In D, a type is considered enumerable (Range is more used as the concept in D) if:
 +
 
 +
* implements three methods named popFront, empty and front or,
 +
* implements a special operator named opApply;
 +
 
 +
''The C# Way''
 +
<syntaxhighlight lang="C#">
 +
class Prime: IEnumerable {
 +
    object[] items = {1, 3, 5, 7};
 +
                                                                         
 +
    public IEnumerator GetEnumerator() {
 +
      foreach (object o in items) {
 +
          // Return the current element and then on next function call
 +
          // resume from next element rather than starting all over again;
 +
          yield return o;
 +
      }
 +
    }
 +
}
 +
 +
[TestFixture] public class TestIterator {
 +
    [Test] public void prime_test() {
 +
      Prime primes = new Prime();
 +
      int[] expected = {1, 3, 5, 7};
 +
      int i = 0;
 +
                                                                         
 +
      foreach (object p in primes) {
 +
          Assert.AreEqual(p, expected[i++]);
 +
      }
 +
    }
 +
}
 +
</syntaxhighlight>
 +
''The D Way (front, popFront, empty)''
 +
<syntaxhighlight lang="D">
 +
class Prime {
 +
    private int[] p = [1, 3, 5, 7];
 +
    @property bool empty() {
 +
      return p.length == 0;
 +
    }
 +
    @property int front() {
 +
      return p[0];
 +
    }
 +
    void popFront() {
 +
      p = p[1..$];
 +
    }
 +
}
 +
 
 +
class TestIterator {
 +
  mixin UnitTest;
 +
 +
  @Test prime() {
 +
      Prime primes = new Prime();
 +
      int[] expected = [1, 3, 5, 7];
 +
      int i;
 +
      for (; !primes.empty; primes.popFront()) {
 +
        assert(primes.front == expected[i++]);
 +
      }
 +
  }
 +
}
 +
</syntaxhighlight>
 +
''The D Way (opApply)''
 +
 
 +
'''TO DO'''
  
 
== Code Attributes ==
 
== Code Attributes ==
Line 330: Line 800:
 
''The C# Way''
 
''The C# Way''
  
class Person {
+
<syntaxhighlight lang="C#">
  public Person() {
+
class Person
    Name = "Default Name";
+
{
  }
+
    public Person()
   public string Name { get; set; }
+
    {
 +
        Name = "Default Name";
 +
    }
 +
    
 +
    public string Name { get; set; }
 
  }
 
  }
 +
</syntaxhighlight>
 +
 +
''The D Way''
 +
<syntaxhighlight lang="C#">
 +
class Person
 +
{
 +
    private string _name;
 +
   
 +
    this()
 +
    {
 +
        _name = "Default Name";
 +
    }
 +
 +
    @property string name()
 +
    {
 +
        return _name;
 +
    }
 +
 +
    @property void name(string n)
 +
    {
 +
        _name = n;
 +
    }
 +
}
 +
</syntaxhighlight>
 +
 +
= Statements =
 +
 +
== Declaring variables ==
 +
''The C# Way''
 +
<syntaxhighlight lang="C#">
 +
int x;
 +
string text = "abc";
 +
const double PI = 3.14;
 +
</syntaxhighlight>
 +
''The D Way''
 +
<syntaxhighlight lang="D">
 +
int x;
 +
string text = "abc";
 +
enum double PI = 3.14;
 +
</syntaxhighlight>
 +
 +
== Expressions ==
 +
''The C# Way''
 +
<syntaxhighlight lang="C#">
 +
double average = (a + b) / 2; // assignment expression
 +
foo.Bar(); // method call
 +
 +
SomeClass c = new SomeClass(); // class initialization
 +
SomeStruct s = new SomeStruct(); //struct initialization
 +
 +
List<int> list = new List<int>();
 +
Dictionary<string, int> = new Dictionary<string, int>();
 +
</syntaxhighlight>
 +
''The D Way''
 +
<syntaxhighlight lang="D">
 +
double average = (a + b) / 2;  // assignment expression
 +
foo.bar(); // method call
 +
 +
SomeClass c = new SomeClass(); // class initialization
 +
SomeStruct s = SomeStruct(); //struct initialization
 +
 +
List!int list = new List!int();
 +
Dictionary!(string, int) = new Dictionary!(string, int);
 +
</syntaxhighlight>
 +
Differences:
 +
* Structs in D are initialized without the <code>new</code> keyword.
 +
* Generic types in D are initialized using <code>!</code> and by specifing types between parentheses. If there is only one specialization, parentheses can be omitted.
 +
 +
== Using namespaces ==
 +
 +
D is importing modules instead of using namespaces. A collection of modules in D in named `package`. There is one-to-one correspondence between D modules and filenames and one-to-one correspondence between packages and folders.
 +
 +
''The C# Way''
 +
<syntaxhighlight lang="C#">
 +
using System;
 +
using System.Collections;
 +
</syntaxhighlight>
 +
''The D Way''
 +
<syntaxhighlight lang="D">
 +
import system;
 +
//this will try to import 'system.d' from the current folder.
 +
//if 'system' is a folder name, it will try to import a special file named 'package.d'
 +
import system.collections;
 +
//this will import the file 'collections.d' from a folder named 'system'.
 +
</syntaxhighlight>
 +
if you are creating your own namespaces, the closest match for simulating C# namespaces is a trick used to import C++ code in D:
 +
 +
''The D Way''
 +
<syntaxhighlight lang="D">
 +
extern (C++, MyNamespace) {
 +
    class MyClass { ...}
 +
}
 +
//Fully qualified name of MyClass is MyNamespace.MyClass
 +
</syntaxhighlight>
 +
 +
== if Statement ==
 +
The <code>if else</code> statement has the same structure as in C#:
 +
 +
''The C# Way''
 +
<syntaxhighlight lang="C#">
 +
bool b;
 +
if (b) { ... }
 +
 +
int a;
 +
if (a == 10) { .... } else { ... }
 +
 +
SomeClass c;
 +
if (c == null) { ... }
 +
void* p;
 +
if (p != IntPtr.Zero) { ... }
 +
 +
string s = string.Empty;
 +
if (string.IsNullOrEmpty(s)) { ... }
 +
</syntaxhighlight>
 +
 +
''The D Way''
 +
<syntaxhighlight lang="D">
 +
bool b;
 +
if (b) { ... }
 +
 +
int a;
 +
if (a == 10) { .... } else { ... }
 +
if (a) { ... }
 +
 +
SomeClass c;
 +
if (c is null) { ... }
 +
if (!c) { ... }
 +
 +
void* p;
 +
if (p) { ... }
 +
 +
string s = "";
 +
if(s.length == 0) { ... }
 +
</syntaxhighlight>
 +
 +
Differences:
 +
 +
* null values are compared using is operator. The is operator has a very different meaning in D.
 +
* In D, the conditional expression can be any other type than bool
 +
 +
== switch Statement ==
 +
The <code>switch</code>  case statement has exactly the same structure as in C#, except that D provides a final switch statement intended to use with enum types.
 +
 +
''The C# Way''
 +
<syntaxhighlight lang="C#">
 +
enum Color { Blue, Red, Green }
 +
 +
Color c;
 +
switch (c) {
 +
    case Color.Blue: ... break;
 +
    case Color.Red:  ... break;
 +
    default: ... break;
 +
}
 +
</syntaxhighlight>
 +
''The D Way''
 +
<syntaxhighlight lang="D">
 +
enum Color { Blue, Red, Green }
 +
 +
Color c;
 +
final switch (c) {
 +
    case Color.Blue: ... break;
 +
    case Color.Red:  ... break;
 +
    //compilation error if the switch statement does not treat all members of Color.
 +
}
 +
</syntaxhighlight>
 +
or [http://p0nce.github.io/d-idioms/#Qualified-switch-using-with using with]
  
 
''The D Way''
 
''The D Way''
class Person {
+
 
  private string _name;
+
<syntaxhighlight lang="D">
  this() {
+
// The members of enums should be camelCased
    _name = "Default Name";
+
enum Color { blue, red, green }
  }
+
 +
Color c;
 
   
 
   
  @property string name() {
+
final switch() with (Color){
    return _name;
+
    case blue: ... break;
  }
+
    case red:  ... break;
  @property void name(string n) {
+
    case green:  ... break;
    _name = n;
+
    //compilation error if the switch statement does not treat all members of Color.
  }
+
}
}
+
</syntaxhighlight>
 +
 
 +
== Iteration statements ==
 +
'''TO DO'''
 +
 
 +
== Jump statements ==
 +
'''TO DO'''
 +
 
 +
= Exception handling =
 +
 
 +
'''TO DO'''
 +
 
 +
= Argument Checking =
 +
'''TO DO'''
 +
 
 +
= nameof =
 +
''[https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/nameof The C# Way]''
 +
 
 +
<syntaxhighlight lang="C#">
 +
using System;
 +
using System.Diagnostics;
 +
 
 +
class Program
 +
{
 +
    static void Main()
 +
    {
 +
        int myIdentifier;
 +
        Debug.Assert(nameof(myIdentifier) == "myIdentifier");
 +
    }
 +
}
 +
</syntaxhighlight>
 +
 
 +
''The D Way'' (Credit: https://forum.dlang.org/post/wcatxfktmqlgihzdmjtt@forum.dlang.org)
 +
 
 +
<syntaxhighlight lang="D">
 +
template nameOf(alias nameType)
 +
{
 +
    enum string nameOf = __traits(identifier, nameType);
 +
}
 +
 
 +
void main()
 +
{
 +
    int myIdentifier;
 +
    assert(nameOf!myIdentifier == "myIdentifier");
 +
}
 +
</syntaxhighlight>
  
 
= Resources =
 
= Resources =
 
* [http://www.youtube.com/watch?v=6_xdfSVRrKo C# to D video-presentation]
 
* [http://www.youtube.com/watch?v=6_xdfSVRrKo C# to D video-presentation]
 
+
* [https://github.com/rumbu13/sharp/blob/master/cstod.md Original article]
 +
* [https://github.com/ilmanzo/DLangKoans/ D Koans]
 +
* [https://github.com/taylorh140/Calling-NET-from-D Calling .NET from D]
 
[[Category:Languages versus D]]
 
[[Category:Languages versus D]]

Latest revision as of 23:54, 9 May 2021

Introduction: Hello world

The C# Way

file: hello.cs

using System;
class Hello
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello world!");
    }
}

The D Way

file: hello.d

import std.stdio;
void main(string[] args)
{
    writeln("Hello world!");
}

Things we have learnt so far:

  • standard file extension for D source code files is .d
  • we import a module instead of using a namespace;
  • methods (functions) can be top-level, that is, not declared inside a class; and called without being prefixed with an object/class or class name (e.g. writeln)
  • writeln is the D equivalent for C#'s System.Console.WriteLine;
  • some 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);

Documentation Comments

The C# Way

 /// <summary>
 /// Documentation comment
 /// </summary>
 /// <param name="foo">...</param>
 /// <param name="bar">...</param>
 /// <returns>...</returns>

The D Way

 /**
  Documentation comment
 
  Params:  foo = param description
           bar = param description
 
  Returns: return value
 
  Throws:  Exception 
 */

Compilation

Simple compilation

The C# Way

With mcs mono compiler:

$ mcs source1.cs source2.cs -out:program.exe

The D Way

With DMD Digital Mars compiler:

$ dmd source1.d source2.d -ofprogram

Conditional Compilation Directives

The C# Way

Defines one or more conditional symbols (short: -d)

-define:S1[;S2]
// compile with: /define:xx
// or uncomment the next line:
//#define xx
 
[Test] public void define_test() {
    int x = 0;
    #if (xx)
       x = 5;
    #else
       x = 1;
    #endif
    Assert.AreEqual(x, 5);
}

The D Way

-version=S1
// compile with: -version=xx
// or uncomment the next line:
//version = xx
 
unittest {
    int x = 0;
    version(xx) {
       x = 5;
    }
    else {
       x = 1;
    }
    assert( x == 5 );
}

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_).


Type C# Way D Way
Class PascalCase PascalCase
Struct PascalCase PascalCase
class method PascalCase camelCase
class property PascalCase camelCase
enum declaration PascalCase PascalCase
enum value PascalCase camelCase
const UPPER_CASE -
local variable camelCase 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 as byte;
  • The 8 bit unsigned integer from C# byte is written in D as ubyte;
  • There is no type equivalence for decimal
  • There are three types of char in D: char, wchar and dchar, 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 D wchar.
  • There are also three types of string in D: string, wstring and dstring. The direct equivalent of C# string is in fact D wstring. 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 are ifloat, idouble, ireal;

Literals

TO DO

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[] longFruits = 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

For D see D Koans and dlang

The C# Way

List<T> behaves 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 Permission
{
    read,
    write,
    all = read | wite
}

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;
}
... 
 
// preferred syntax
auto t1 = TimeOfDay(8, 30);  
assert(t1.hour == 8);
assert(t1.minute == 30);
 
// alternate C syntax, deprecated
TimeOfDay t2 = {9, 45};      
assert(t2.hour == 9);
 
// with designated initializers
auto t3 = {minute: 20, hour: 8};
assert(t3.hour == 8);
assert(t3.minute == 20);
  
auto t4 = TimeOfDay(10);     // not all members need to be specified
assert(t4.hour == 10);
assert(t4.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 a 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()
    {
        writeln("hello");
    }
}

Classes

The C# Way

class A {
    private int myValue;  // not accessible from outside
                                                                                    
    public A(int startValue) { // constructor
       myValue = startValue;
    }
    public int Value {
       get { return myValue; }
    }
                                                                                    
}
                                                                                    
class B: A {
    public B(): base(3) {}
                                                                                    
    public int GetDoubleValue() {
       //return myValue * 2;  // Error: myValue is inaccessible due to its protection level
       return this.Value * 2;
    }
}
                                                                                    
[TestFixture] public class AboutClasses {
    [Test]public void inheritance() {
       B instance = new B();
       Assert.AreEqual(instance.GetDoubleValue(), 6);
    }
}

The D Way

class A {
   private int myValue;  // not accessible from outside
 
   this(int startValue) { // constructor
     myValue = startValue;
   }
}
 
class B: A {
   this() {
     super(3);   // call constructor on parent class A
   } 
 
   auto getDoubleValue() {
     return myValue * 2;  // B doesn't have this field but can access the value
   }
}
 
class AboutClasses {
  
   @Test public void inheritance() {
      auto instance = new B;
      assertEquals(instance.getDoubleValue(), 6);
    }
}

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) {}
}
// use
A a = new A();
a.Foo<int>(1);
 
 
public class C<T> where T: class { }
...                                           
C<A> c = new C<A>();
// C<int> i = new C<int>(); compiler error: int isn't a class
 
 
public class X { }
public class D<T> where T: X {}
 
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) {}
// use
foo!int(3);
foo!string("bar");
 
class X {} 
class C(T) if is(T == class) {}
 
// use
C c = new C!X;
// C c = new C!int;  compiler error: int isn't a class
 
 
class D(T) if is(T : X) {}
auto d = new D!X;
//auto d = new D!(A);compiler error: A isn't X

Constraints

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 expression new T() is of type T. If the T class has no constructor 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 (Credit: https://forum.dlang.org/post/dcdtuqyrxpteuaxmvwft@forum.dlang.org):

class Event(T...)
{
    alias CB = void delegate(T);
    CB[] callbacks;

    // NOTE: This implementation uses the `+` operator as it is likely more
    // familiar to C# programmers, but a more idiomatic D operator would be
    // `~`, D's concatenation operator.

    void opOpAssign(string op)(CB handler)
        if (op == "+" || op == "-")
    {
        static if (op == "+")
            callbacks ~= handler;
        else
        {
            import std.algorithm.mutation : remove;
            callbacks = callbacks.remove!(x => x == handler);
        }
    }

    void opOpAssign(string op)(void function(T) handler)
        if (op == "+" || op == "-")
    {
        import std.functional : toDelegate;
        opOpAssign!op(toDelegate(handler));
    }

    void opCall(T args)
    {
        foreach (cb; callbacks)
            cb(args);
    }
}

class Property(T)
{
    private T _value;

    alias event = Event!(T, T);

    auto changing = new event();
    auto changed = new event();

    T value() @property
    {
        return _value;
    }

    T value(T newValue) @property
    {
        if (_value != newValue)
        {
            T oldValue = _value;
            changing(_value, newValue);
            _value = newValue;
            changed(oldValue, _value);
        }
        return _value;
    }
}

void main()
{
    auto test = new Property!(int)();

    int changingCount;
    void onChanging(int oldValue, int newValue)
    {
        changingCount++;
    }
    test.changing += &onChanging;

    int changedCount;
    void onChanged(int oldValue, int newValue)
    {
        changedCount++;
    }
    test.changed += &onChanged;
    
    test.value = 1;  // value changed from 0 to 1
    test.value = 1;  // value not changed
    test.value = 2;  // value changed from 1 to 2

    assert(changingCount == 2);
    assert(changedCount == 2);
}

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

Boxing, otherwise known as wrapping, is the process of placing a primitive type within an object so that the primitive can be used as a reference object (wikipedia)

Value types in D are not boxed or unboxed automatically and do not inherit from ValueType. Also, you cannot implement interfaces for value types. Depending on your purpose, there are alternatives to boxing in D:

  • if you just want to have a container for several types you can use std.variant
  • if you want to write general purpose method with object parameters, use generics

The C# Way

[Test]public void boxing_test() {
    int a = 1;
    float b = 19.64f;
                                      
    object o = a;
    Assert.AreEqual(o, 1);
    Assert.AreEqual(tos(o), "1");
                                      
    o = b;
    Assert.AreEqual(o, 19.64f);
                                      
    b = (float)o;
    Assert.AreEqual(b, 19.64f);
    Assert.AreEqual(tos(o), "19.64");
}
private string tos(object obj) {
    return obj.ToString();
}

The D Way

import std.variant;

@Test void boxing_test() {
    int a = 1; 
    float b = 19.64f;
 
    Variant o = a;
    assert(o.type == typeid(int));
    assert(tos(o) == "1");
                                   
    v = b;
    auto x = v.get!(float);
    assert(x == 19.64f);
}
                                   
string tos(T)(T obj) {
    return to!string(obj);
}

Nullable types

Value types are not nullable in the D language. But there is a solution in the standard library module typecons - templated struct Nullable - to simulate this behavior. This is the same solution as in C#, although C# provides the shorthand syntax of "Type? variable-name", which can hide the usage of templated struct System.Nullable<>.

The C# Way

int? a = null; // short hand for: System.Nullable<int> a = null;
// attempting to use a.Value will cause an exception to be thrown
int? num = 10;
if (num.HasValue) {
    System.Console.WriteLine(3 * num.Value); // prints 30
} else {
    System.Console.WriteLine("num = Null");
}

The D Way

import std.typecons;

Nullable!int a; // a is null and cannot be used as an int until assigned a value
                // attempting to use a as an int will cause an exception to be thrown
Nullable!int num = 10;
if ( ! num.isNull) {
    writeln(num.get * 3);  // prints 30
    writeln(num * 3);      // prints 30 - get not required (see below)
} else {
    writeln("num = Null");
}
int y = num;  // get property not required due to "alias get this" inside Nullable for 
              // automatic compiler conversion to int when Nullable!(int) is not appropriate
int z = num.isNull ? int.init : num;
num.nullify();    // num set to null - cannot be used as an int until re-assigned

Enumerable types

In C#, a type can be considered enumerable if implements the IEnumerable interface. In D, a type is considered enumerable (Range is more used as the concept in D) if:

  • implements three methods named popFront, empty and front or,
  • implements a special operator named opApply;

The C# Way

class Prime: IEnumerable {
    object[] items = {1, 3, 5, 7};
                                                                           
    public IEnumerator GetEnumerator() {
       foreach (object o in items) {
          // Return the current element and then on next function call
          // resume from next element rather than starting all over again;
          yield return o;
       }
    }
}
 
[TestFixture] public class TestIterator {
    [Test] public void prime_test() {
       Prime primes = new Prime();
       int[] expected = {1, 3, 5, 7};
       int i = 0;
                                                                           
       foreach (object p in primes) {
          Assert.AreEqual(p, expected[i++]);
       }
    }
}

The D Way (front, popFront, empty)

class Prime {
    private int[] p = [1, 3, 5, 7];
    @property bool empty() {
       return p.length == 0;
    }
    @property int front() {
       return p[0];
    }
    void popFront() {
       p = p[1..$];
    }
} 

class TestIterator {
   mixin UnitTest;
 
   @Test prime() {
      Prime primes = new Prime();
      int[] expected = [1, 3, 5, 7];
      int i;
      for (; !primes.empty; primes.popFront()) {
         assert(primes.front == expected[i++]);
      }
   }
}

The D Way (opApply)

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;
    }
}

Statements

Declaring variables

The C# Way

int x;
string text = "abc";
const double PI = 3.14;

The D Way

int x;
string text = "abc";
enum double PI = 3.14;

Expressions

The C# Way

double average = (a + b) / 2; // assignment expression
foo.Bar(); // method call
 
SomeClass c = new SomeClass(); // class initialization
SomeStruct s = new SomeStruct(); //struct initialization
 
List<int> list = new List<int>();
Dictionary<string, int> = new Dictionary<string, int>();

The D Way

double average = (a + b) / 2;  // assignment expression
foo.bar(); // method call
 
SomeClass c = new SomeClass(); // class initialization
SomeStruct s = SomeStruct(); //struct initialization
 
List!int list = new List!int();
Dictionary!(string, int) = new Dictionary!(string, int);

Differences:

  • Structs in D are initialized without the new keyword.
  • Generic types in D are initialized using ! and by specifing types between parentheses. If there is only one specialization, parentheses can be omitted.

Using namespaces

D is importing modules instead of using namespaces. A collection of modules in D in named `package`. There is one-to-one correspondence between D modules and filenames and one-to-one correspondence between packages and folders.

The C# Way

using System;
using System.Collections;

The D Way

import system;
//this will try to import 'system.d' from the current folder. 
//if 'system' is a folder name, it will try to import a special file named 'package.d'
import system.collections;
//this will import the file 'collections.d' from a folder named 'system'.

if you are creating your own namespaces, the closest match for simulating C# namespaces is a trick used to import C++ code in D:

The D Way

extern (C++, MyNamespace) {
    class MyClass { ...}
}
//Fully qualified name of MyClass is MyNamespace.MyClass

if Statement

The if else statement has the same structure as in C#:

The C# Way

bool b; 
if (b) { ... }
 
int a;
if (a == 10) { .... } else { ... }
 
SomeClass c;
if (c == null) { ... }
void* p;
if (p != IntPtr.Zero) { ... }
 
string s = string.Empty;
if (string.IsNullOrEmpty(s)) { ... }

The D Way

bool b; 
if (b) { ... }
 
int a;
if (a == 10) { .... } else { ... }
if (a) { ... }
 
SomeClass c;
if (c is null) { ... }
if (!c) { ... }
 
void* p;
if (p) { ... }
 
string s = "";
if(s.length == 0) { ... }

Differences:

  • null values are compared using is operator. The is operator has a very different meaning in D.
  • In D, the conditional expression can be any other type than bool

switch Statement

The switch case statement has exactly the same structure as in C#, except that D provides a final switch statement intended to use with enum types.

The C# Way

enum Color { Blue, Red, Green }
 
Color c;
switch (c) {
    case Color.Blue: ... break;
    case Color.Red:  ... break;
    default: ... break;
}

The D Way

enum Color { Blue, Red, Green }
 
Color c;
final switch (c) {
    case Color.Blue: ... break;
    case Color.Red:  ... break;
    //compilation error if the switch statement does not treat all members of Color.
}

or using with

The D Way

// The members of enums should be camelCased
enum Color { blue, red, green }
 
Color c;
 
final switch() with (Color){
    case blue: ... break;
    case red:  ... break;
    case green:  ... break;
    //compilation error if the switch statement does not treat all members of Color.
}

Iteration statements

TO DO

Jump statements

TO DO

Exception handling

TO DO

Argument Checking

TO DO

nameof

The C# Way

using System;
using System.Diagnostics;

class Program
{
    static void Main()
    {
        int myIdentifier;
        Debug.Assert(nameof(myIdentifier) == "myIdentifier");
    }
}

The D Way (Credit: https://forum.dlang.org/post/wcatxfktmqlgihzdmjtt@forum.dlang.org)

template nameOf(alias nameType)
{
    enum string nameOf = __traits(identifier, nameType);
}

void main()
{
    int myIdentifier;
    assert(nameOf!myIdentifier == "myIdentifier");
}

Resources