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

From D Wiki
Jump to: navigation, search
(Exception handling)
Line 615: Line 615:
 
= Statements =
 
= Statements =
 
== Declaring variables ==
 
== Declaring variables ==
'''
+
''The C# Way''
  
 
  int x;
 
  int x;
 
  string text = "abc";
 
  string text = "abc";
  const double PI = 10;
+
  const double PI = 3.14;
 +
 
 +
''The D Way''
 +
 
 +
int x;
 +
string text = "abc";
 +
enum double PI = 3.14;
 +
 
  
 
== Expressions ==
 
== Expressions ==
''TO DO''
+
'''TO DO'''
  
 
== Using namespaces ==
 
== Using namespaces ==
''TO DO''
+
'''TO DO'''
  
 
== Selection statements ==
 
== Selection statements ==
''TO DO''
+
'''TO DO'''
 
== Iteration statements ==
 
== Iteration statements ==
''TO DO''
+
'''TO DO'''
 
== Jump statements ==
 
== Jump statements ==
''TO DO''
+
'''TO DO'''
  
 
= Exception handling =
 
= Exception handling =
Line 639: Line 646:
  
 
= Argument Checking =
 
= Argument Checking =
 
+
'''TO DO'''
  
 
= Resources =
 
= Resources =

Revision as of 20:58, 27 April 2015

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 of using 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);

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

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

The D Way

$ 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

TO DO


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

For D see D Koans and dlang

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() {
     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);   // what happens here ?
  } 

  auto getDoubleValue() {
    return myValue * 2;  // B doesn't have this field but..
  }
}

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

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

@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 D language. There are solutions in the standard library to simulate this behaviour

The C# Way

int? num = null;
if (num.HasValue) {
   System.Console.WriteLine("num = " + num.Value);
} else {
   System.Console.WriteLine("num = Null");
}

The D Way

Nullable!int num = 10;
if (num.isNull) {
   writeln(num.get());
}
int y = num.get();
int z = num.isNull ? int.init : num.get();
num.nullify();

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 myList = new Prime();
      int[] v = {1, 3, 5, 7};
      int i = 0;
                                                                          
      foreach (object s in myList) {
         Assert.AreEqual(s, v[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 prime = new Prime();
     int[] p = [1, 3, 5, 7];
     int i;
     for (; !prime.empty; prime.popFront()) {
        assert(prime.front == p[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

TO DO

Using namespaces

TO DO

Selection statements

TO DO

Iteration statements

TO DO

Jump statements

TO DO

Exception handling

TO DO

Argument Checking

TO DO

Resources