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

From D Wiki
Jump to: navigation, search
(Boxing)
Line 413: Line 413:
 
     _name = n;
 
     _name = n;
 
   }
 
   }
 +
}
 +
 +
= Compilation Directives =
 +
== 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);
 
  }
 
  }
  

Revision as of 21:03, 23 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);


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;

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

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

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

Compilation Directives

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

Resources