Contents
-
Vala for C# Programmers
- Source Files
- Compilation
- Naming Conventions
- Main Entry Point
- System Namespace
- Value Types
- Verbatim String Literals
- Documentation Comments
- Object Base Class
- Method Overloading
- Multiple Constructors
- Constructor Chaining
- Delegates / Lambdas
- Events
- Interfaces
- Enums
- Struct Initialization
- Multi-Dimensional Arrays
- Nullable Types
- Code Attributes
- Properties
- Exceptions
- Argument Checking
- Unsafe Code and Pointers
- Conditional Compilation Directives
- Resource Disposing
- Memory Management
- Asynchronous Calls
- Static Constructors
- External Methods
- Reflection
- Not Available
- Collections
- Indexers
- IO, Network Sockets
- Console Input / Output
- GTK+ Demo App
- Bindings
Vala for C# Programmers
This page describes the syntax differences, not the similarities between Vala and C#. It is intended to be a quick introduction to Vala for programmers who already know C#.
Source Files
C#: *.cs
Vala: *.vala
Compilation
C# (.NET / Mono): compiled to CIL
> csc source1.cs source2.cs /out:program.exe $ gmcs source1.cs source2.cs -out:program.exe
Vala: compiled to native code via C code as intermediate code
$ valac source1.vala source2.vala -o program
Vala's standard object system is GObject, compiled Vala libraries are valid C libraries.
Using Packages
Mono: -pkg:
$ gmcs source.cs -pkg:gtk-sharp-2.0 -out:program.exe
Vala: --pkg
$ valac source.vala --pkg gtk+-2.0 -o program
Naming Conventions
C#
classes, structs, delegate types: CamelCase
methods, properties, events: CamelCase
local variables, fields: mixedCamelCase (sometimes lower_case)
constants, enum values: CamelCase
Vala
classes, structs, delegate types: CamelCase
methods, properties, signals: lower_case
local variables, fields: lower_case
constants, enum values: UPPER_CASE
Main Entry Point
C#: Main, must be inside a class
Vala: main, may be outside a class
System Namespace
C#: most important namespace is System, not imported by default
Vala: most important namespace is GLib, implicitly imported by default
Value Types
sizes of standard types (int, long, ...) are architecture dependent
additional Vala types int8, int16, int32, int64 (signed), uint8, uint16, uint32, uint64 (unsigned) with architecture independent guaranteed sizes
no byte, sbyte (use uint8, int8 instead)
no decimal
C# char is UCS-2, not Vala's char, but similar to Vala's UCS-4 unichar
Verbatim String Literals
C#: @"verbatim string"
Vala: """verbatim string"""
Documentation Comments
C#:
/// <summary>
/// Documentation comment
/// </summary>
/// <param name="foo">...</param>
/// <param name="bar">...</param>
/// <returns>...</returns>
Vala: Valadoc comments
/**
* Documentation comment
*
* @param foo ...
* @param bar ...
* @return ...
*/
Object Base Class
C#: implicit inheritance from object (System.Object)
class Foo
{
// ...
}
Vala: no implicit inheritance from Object (GLib.Object)
class Foo : Object {
// ...
}
What happens if you don't inherit from Object? Nothing terrible. These classes will be slightly more lightweight, however, they will lack some features such as property change notifications, and your objects won't have a common base class. Usually inheriting from Object is what you want.
Method Overloading
C#
class Demo
{
public void Draw(string text) { }
public void Draw(Shape shape) { }
/* Method overloading + chaining for convenience methods with less arguments */
void F(int x, string s, double z) { }
void F(int x, string s)
{
F(x, s, 0.5);
}
void F(int x)
{
F(x, "hello");
}
}
Vala: no method overloading, use different names instead or default values for arguments
class Demo : Object {
public void draw_text (string text) {
}
public void draw_shape (Shape shape) {
}
/* Argument default values, available in Vala, planned for C# 4.0 */
void f (int x, string s = "hello", double z = 0.5) {
}
}
Multiple Constructors
C#: constructor overloading
class Foo
{
public Foo() { }
public Foo(int foo) { }
public Foo(string bar) { }
}
new Foo();
new Foo(42);
new Foo("hello");
Vala: no constructor overloading, named constructors instead
class Foo : Object {
public Foo () { }
public Foo.with_foo (int foo) { }
public Foo.from_bar (string bar) { }
}
new Foo ();
new Foo.with_foo (42);
new Foo.from_bar ("hello");
Constructor Chaining
Base Constructor Chain-Up
C#
class Foo : Bar
{
public Foo() : base(42)
{
// ...
}
}
Vala: base call inside constructor
class Foo : Bar {
public Foo () {
base (42);
// ...
}
}
Multiple Constructor Chaining
C#
class Foo
{
public Foo() : this("bar") { }
public Foo(string bar) { }
}
Vala
class Foo : Object {
public Foo () {
this.with_bar ("bar");
}
public Foo.with_bar (string bar) {
}
}
Delegates / Lambdas
C#
delegate void DelegateType(string s);
void Method(string s)
{
Console.WriteLine(s);
}
// Original style
DelegateType d1 = new DelegateType(Method);
// Direct method assignment
DelegateType d2 = Method;
// C# 2.0 style
DelegateType d3 = delegate(string s) => { Console.WriteLine(s); };
// Lambda Expression with types (C# 3.0)
DelegateType d4 = (string s) => { Console.WriteLine(s); };
// Lambda Expression without types (C# 3.0)
DelegateType d5 = (s) => { Console.WriteLine(s); };
Vala: either lambda expression without types or direct method assignment (without new ...), no C# 2.0 style
delegate void DelegateType (string s);
void method (string s) {
stdout.printf ("%s\n", s);
}
DelegateType d1 = method;
DelegateType d2 = (s) => { stdout.printf ("%s\n", s); };
Events
C#: events
using System;
delegate void SomeEventHandler(object sender, int i);
class Foo
{
public event SomeEventHandler SomeEvent;
public void RaiseSomeEvent(int i)
{
if (SomeEvent != null) SomeEvent(this, i);
}
}
class Demo
{
static void OnSomeEvent(object sender, int i)
{
Console.WriteLine("Handler A: " + i);
}
static void Main()
{
var foo = new Foo();
foo.SomeEvent += OnSomeEvent;
foo.SomeEvent += (s, i) => Console.WriteLine("Handler B: " + i);
foo.RaiseSomeEvent(42);
foo.SomeEvent -= OnSomeEvent;
}
}
Vala: signals
class Foo {
public signal void some_event (int i);
}
class Demo {
static void on_some_event (Foo sender, int i) {
stdout.printf ("Handler A: %d\n", i);
}
static void main () {
var foo = new Foo ();
foo.some_event.connect (on_some_event);
foo.some_event.connect ((s, i) => stdout.printf ("Handler B: %d\n", i));
foo.some_event (42);
foo.some_event.disconnect (on_some_event);
}
}
No extra delegate declaration, signals can be emitted directly (no null checking necessary). Use .connect() and .disconnect() instead of += and -=. Both is possible in Vala, however += and -= may become deprecated for signal connection.
Signals do not support add {} and remove {} blocks.
Interfaces
C#
interface IFoo
{
void Foo(int i);
int Bar(string s, double d);
}
Vala: public abstract necessary
interface Foo {
public abstract void foo (int i);
public abstract int bar (string s, double d);
}
Why? Because Vala interfaces may have non-abstract methods (i.e. methods with implementations) and private methods! This means Vala interfaces can be used as mixins (restricted form of multiple inheritance).
'I'-prefix not common in GObject world but allowed
C#: interface inheritance
interface IfaceA
{
void MethodA();
}
interface IfaceB : IfaceA
{
void MethodB();
}
class Demo : IfaceB
{
public void MethodA() { }
public void MethodB() { }
}
Vala: interface prerequisites
interface IfaceA : Object {
public abstract void method_a ();
}
interface IfaceB : Object, IfaceA {
public abstract void method_b ();
}
class Demo : Object, IfaceA, IfaceB {
public void method_a () { }
public void method_b () { }
}
Interfaces in Vala may not inherit from other interfaces, but they may declare other interfaces to be prerequisites, which works in roughly the same way. Interfaces may also have a class as a prerequisite. This is often used to ensure that an instance of an interface is also an Object subclass. The fact that interfaces can not inherit from other interfaces is mostly only a technical distinction - in practice Vala's system works the same as C# in this area, but with the extra feature of prerequsite classes.
Enums
Vala enums may have methods:
enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
public bool is_hot () {
return this == SUMMER;
}
}
In C# this can only be achieved by extension methods:
enum Season { Spring, Summer, Autumn, Winter }
static class SeasonExtensions
{
public static bool IsHot(this Season season)
{
return season == Season.Summer;
}
}
Struct Initialization
C#
var p1 = new Point();
var p2 = new Point() { X = 2, Y = 3 };
Point p3;
p3.X = 2;
p3.Y = 3;
Vala: structs are instantiated without using the new operator.
var p1 = Point ();
var p2 = Point () { x = 2, y = 3 };
Point p3 = { 2, 3 };
Vala structs must be initialized before first use. A Vala struct cannot implement interfaces.
Multi-Dimensional Arrays
C#: both rectangular [,] and jagged [][] multi-dimensional arrays
Vala: only rectangular [,] multi-dimensional arrays, jagged array support planned
Nullable Types
C#: mark nullable value types
int? i = null;
Vala: mark nullable reference type arguments and return values of methods. They are non-nullable by default!
Foo? method (Foo? foo, Bar bar) {
return null;
}
In this example: foo and return value may be null, bar must be non-null. Checked at run-time, and to some extent at compile time.
Conclusion: same syntax (? type modifier), different meanings.
Code Attributes
C#: self-definable
Vala: built into the compiler, mostly used for bindings or D-Bus interfaces. The most prominent attribute for bindings is [CCode (...)].
Properties
Default Values for Auto-Implemented Properties
C#
class Person
{
public Person()
{
Name = "Default Name";
}
public string Name { get; set; }
}
Vala
class Person : Object {
public string name { get; set; default = "Default Name"; }
}
Setting in constructor works as well, of course.
Property Change Notifications
C#: implement INotifyPropertyChanged
using System.ComponentModel;
class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Name");
}
}
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
static void Main()
{
var person = new Person();
person.PropertyChanged += (sender, e) =>
{
System.Console.WriteLine("Property '{0}' changed", e.PropertyName);
};
person.Name = "Foo";
person.Name = "Bar";
}
}
Vala: connect to notify signal
Every instance of a class derived from Object has a signal called notify. This signal gets emitted every time a property of its object changes.
class Person : Object {
public string name { get; set; }
}
void main () {
var person = new Person ();
person.notify.connect ((sender, property) => {
stdout.printf ("Property '%s' changed\n", property.name);
});
person.name = "Foo";
person.name = "Bar";
}
If you're only interested in change notifications of a single property you can use this syntax:
person.notify["name"].connect ((sender, property) => {
stdout.printf ("name has changed\n");
});
Note that in this case you must use the string representation of the property name where underscores are replaced by dashes: my_property_name becomes "my-property-name" in this representation, which is the GObject property naming convention.
Change notifications can be disabled with a CCode attribute tag immedietely before the declaration of the property:
class MyObject : Object {
// notify signal is NOT emitted upon changes in the property
[CCode (notify = false)]
public int without_notification { get; set; }
// notify signal is emitted upon changes in the property
public int with_notification { get; set; }
}
Exceptions
C#: unchecked exceptions, class-based
[Serializable()]
public class MyException : System.Exception
{
public MyException() { }
public MyException(string message) { }
public MyException(string message, System.Exception inner) { }
protected MyException(System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) { }
}
void Method()
{
throw new MyException("not enough foo");
}
try {
Method();
} catch (MyException e) {
Console.Error.WriteLine(e.Message);
}
Vala: checked exceptions, Vala terminology: errors, not class-based, no wrapping
// error domain with multiple error codes instead of exception class
errordomain MyError {
FOO,
BAR
}
// must be declared in method signature, part of the contract
void method () throws MyError {
// error domain, error code, error message
throw new MyError.FOO ("not enough foo");
}
// must be catched or propagated, compiler warning if ignored
try {
method ();
} catch (MyError e) {
stderr.printf ("Error: %s\n", e.message);
}
Although the compiler emits warnings for ignored errors it does not abort the compilation process. This allows prototyping without proper error handling and will hopefully prevent forgotten empty catch blocks.
Argument Checking
C#
void Method(double d, int i, Foo foo)
{
if (d < 0 || d > 1.0)
throw new ArgumentOutOfRangeException();
if (i < 0 || i > 10)
throw new ArgumentOutOfRangeException();
if (foo == null)
throw new ArgumentNullException();
// ...
}
Vala: reference type parameters are implicitly checked for null unless they are marked nullable with ?, so you don't have to check them manually. Methods may have preconditions:
void method (double d, int i, Foo foo)
requires (d >= 0.0 && d <= 1.0)
requires (i >= 0 && i <= 10)
{
// ...
}
Vala additionally supports postcontditions for checking the return value:
int square (int i)
ensures (result >= 0)
{
return i * i;
}
result is a special variable representing the return value.
Use exceptions (errors) for recoverable runtime errors (database errors, I/O errors), use preconditions and assertions (assert (...)) for programming errors such as illegal arguments.
Unsafe Code and Pointers
C#: must be inside unsafe { } block
Vala: may be used everywhere. However, be careful nonetheless.
Conditional Compilation Directives
C#: compiler options -define: or -d:
Vala: compiler options --define or -D
no #define, #undef, #region, #endregion, #line
Resource Disposing
C#: destructors are non-deterministic, implement IDisposable instead, resource control with using
class MyResource : IDisposable
{
public void Dispose()
{
// ...
}
}
/* Usage: */
using (var res = new MyResource()) {
// ...
}
Vala: destructors are deterministic, you can implement the RAII pattern with destructor
class MyResource : Object {
~MyResource () {
// ...
}
}
/* Usage: */
{
var res = new MyResource ();
// ...
}
Resource is disposed as soon as it goes out of scope.
Memory Management
C#: Garbage collection
Vala: Automatic reference counting
This has both advantages and disadvantages. Reference counting is deterministic, but you can form reference cycles in some cases. In these cases you must use weak references in order to break those cycles. The Vala keyword for this is weak.
See Vala's Memory Management Explained
Asynchronous Calls
C#
using System;
class AsyncDemo
{
delegate int BinaryOperator(int a, int b);
static int Adder(int a, int b)
{
return a + b;
}
static void Callback(IAsyncResult r)
{
BinaryOperator adder = (BinaryOperator) r.AsyncState;
Console.WriteLine("Addition completed");
Console.WriteLine("Result was: {0}", adder.EndInvoke(r));
}
static void Main()
{
BinaryOperator adder = Adder;
adder.BeginInvoke(4, 5, Callback, adder);
/* wait */
Console.ReadLine();
}
}
Vala: built-in support for asynchronous methods (async, yield), must be compiled with --pkg gio-2.0
class AsyncDemo {
static async int adder (int a, int b) {
return a + b;
}
static async void start () {
int sum = yield adder (4, 5);
stdout.printf ("Addition completed\n");
stdout.printf ("Result was: %d\n", sum);
}
static void main () {
start ();
/* wait */
var loop = new MainLoop (null, false);
loop.run ();
}
}
Static Constructors
C#: called before the first instance is created or any static members are referenced
class Foo
{
static Foo()
{
System.Console.WriteLine("Static constructor invoked.");
}
}
Vala: static construct { } block. The first time that a class, or any subclass of it, is instantiated, this code is run. It is guaranteed that this code will run exactly once in a program where such a class is used.
class Foo : Object {
static construct {
stdout.printf ("Static constructor invoked.\n");
}
}
Additionally a Vala class can have a class construct { } block. This block will be executed once at the first use of its class, and once at the first use of each subclass of this class.
External Methods
C#
using System;
using System.Runtime.InteropServices;
public class MainClass
{
[DllImport("libfoo.so")]
public static extern int SampleMethod(int x);
static void Main()
{
Console.WriteLine("SampleMethod() returns {0}", SampleMethod(5));
}
}
Vala: CCode attribute, specify original function name (only if it differs from the Vala name)
public class MainClass : Object {
[CCode (cname = "SampleMethod")]
public static extern int sample_method (int x);
static void main () {
stdout.printf ("sample_method () returns %d\n", sample_method (5));
}
}
Pass library name to the compiler with -X -l... (with -X meaning the next option is passed through to the C compiler)
$ valac demo.vala -X -lfoo
You can also pass a C source file containing the external method to the Vala compiler. This allows for mixed Vala and C source code projects.
$ valac demo.vala foo.c
Reflection
Reflection support in Vala is limited. Vala provides run-time type information for its data types (is operator, foo.get_type (), dynamic casting via as operator). For example, you can get the names of types, of signals and properties of a class and of enum values.
class Foo : Object {
public int hello { get; set; }
public int world { get; set; }
public int foo_bar { get; set; }
public signal void action ();
public signal void more_action ();
}
enum Bar {
FEE, FIE, FOE, FUM
}
void main () {
/* Getting type information */
Type type = typeof (Foo);
stdout.printf ("%s\n", type.name ());
/* Instantiation from type */
Foo foo = (Foo) Object.new (type);
/* list properties of a class */
var obj_class = (ObjectClass) typeof (Foo).class_ref ();
var properties = obj_class.list_properties ();
foreach (var prop in properties) {
stdout.printf ("%s\n", prop.name);
}
/* enum value as string */
var enum_class = (EnumClass) typeof (Bar).class_ref ();
string name = enum_class.get_value (Bar.FEE).value_name;
stdout.printf ("Enum value as string: %s\n", name);
/* list signals of a class */
uint[] ids = Signal.list_ids (typeof (Foo));
foreach (uint id in ids) {
stdout.printf ("%s\n", Signal.name (id));
}
}
Libraries built with GObjectIntrospection support can be fully introspected.
Not Available
No LINQ (not planned for 1.0, maybe later) there are one [https://gitlab.com/kosmospredanie/gpseq|realization]
No operator overloading (vala-list)
- No method or constructor overloading (use different method names / named constructors instead, as described above)
- No extension methods
No constraints on generic type parameters (i.e. no where)
- No generic delegates
No conversion operators (i.e. no explicit and implicit)
No partial classes and methods
No sealed classes (planned)
- No static classes (use nested namespaces instead. Vala supports namespace methods, they are implicitly static)
No goto, no labeled statements
- No constructor initializers
- No bounds checking for arrays (optional support planned)
No checked, unchecked, fixed, stackalloc, readonly
Collections
C#: System.Collections.Generic namespace
Vala: Gee namespace, --pkg gee-1.0, http://live.gnome.org/Libgee
Rough equivalents:
System.Collections.Generic |
Gee |
Classes |
|
Dictionary |
HashMap |
HashSet |
HashSet |
LinkedList |
LinkedList |
List |
ArrayList |
Queue |
LinkedList, PriorityQueue |
SortedDictionary |
TreeMap |
Stack |
LinkedList |
Interfaces |
|
ICollection |
Collection |
IComparer |
Comparable |
IDictionary |
Map |
IEnumerable |
Iterable |
IEnumerator |
Iterator |
IList |
List |
|
Queue |
|
Deque |
See Gee Examples
You can access and assign Gee collection items via indexers (e.g. my_map[key] is equivalent to my_map.get (key)). Vala supports an in operator for collections: x in my_collection is equivalent to my_collection.contains (x). This operator also works with strings, even though strings are not collections.
Please note that Libgee checks for errors like the bounds for the index key with an assert, and so it won't raise any catchable SystemException like in C#.
Indexers
C#: usage of this keyword to define indexers
class SampleCollection<T>
{
private T[] arr = new T[100];
public T this[int i]
{
get { return arr[i]; }
set { arr[i] = value; }
}
}
class IndexerDemo
{
static void Main(string[] args)
{
var stringCollection = new SampleCollection<string>();
stringCollection[0] = "Hello, World";
System.Console.WriteLine(stringCollection[0]);
}
}
Vala: implement T get(int i) and void set(int i, T item) methods
class SampleCollection<T> {
private T[] arr = new T[100];
public T get (int i) {
return arr[i];
}
public void set (int i, T item) {
arr[i] = item;
}
}
void main (string[] args) {
var string_collection = new SampleCollection<string> ();
string_collection[0] = "Hello, World";
stdout.printf ("%s\n", string_collection[0]);
}
IO, Network Sockets
C#: System.IO, System.Net.Sockets namespaces
Vala: GLib namespace (imported by default), --pkg gio-2.0, GIO is part of GLib
See GIO Examples, GIO Networking Examples
Console Input / Output
C#
System.Console.WriteLine("Hi!");
System.Console.Write("Please enter your name: ");
var name = System.Console.ReadLine();
System.Console.WriteLine("Welcome, {0}!", name);
Vala
stdout.printf ("Hi!\n");
stdout.printf ("Please enter your name: ");
var name = stdin.read_line ();
stdout.printf ("Welcome, %s!\n", name);
printf uses the same format specifiers as the C function.
GTK+ Demo App
C#
using Gtk;
class Demo : Window
{
public Demo() : base("This is a window")
{
SetDefaultSize(250, 200);
SetPosition(WindowPosition.Center);
DeleteEvent += delegate { Application.Quit(); };
var button = new Button("Click");
Add(button);
ShowAll();
}
static void Main()
{
Application.Init();
new Demo();
Application.Run();
}
}
Vala
using Gtk;
class Demo : Window {
public Demo () {
this.title = "This is a window";
set_default_size (250, 200);
set_position (WindowPosition.CENTER);
this.destroy.connect (Gtk.main_quit);
var button = new Button.with_label ("Click");
add (button);
show_all ();
}
static void main (string[] args) {
Gtk.init (ref args);
new Demo ();
Gtk.main ();
}
}
Vala's GTK+ API is very close to the original GTK+ API. In fact, you're using GTK+ functions directly, only with different syntax.
See GTK+ Examples
Bindings
Mono: runtime bindings (wrappers), GAPI tools, .sources files with XML syntax
Vala: no runtime bindings necessary, Vala method calls are direct C function calls, mapped by *.vapi (Vala API) files with Vala syntax, annotated with attributes like [CCode (...)]
On Unix systems VAPI files are usually installed in
/usr/share/vala/vapi/ or
/usr/local/share/vala/vapi/
Using a vapi file (e.g. foo-1.0.vapi):
$ valac source.vala --pkg foo-1.0
The Vala compiler will additionally look for a corresponding pkg-config file (*.pc) with the same base name (e.g. foo-1.0.pc), usually located in
/usr/lib/pkgconfig/ or
/usr/local/lib/pkgconfig/
and pass its configuration to the C compiler if existing.
VAPI files are either auto-generated for GObject libraries or hand-written for non-GObject libraries.