内容
面向 C# 程序员的 Vala
本页介绍的是语法差异,而不是 Vala 和 C# 之间的相似之处。它旨在为已经了解 C# 的程序员提供对 Vala 的快速介绍。
源文件
C#:*.cs
Vala:*.vala
汇编
C# (.NET / Mono):编译为 CIL
> csc source1.cs source2.cs /out:program.exe $ gmcs source1.cs source2.cs -out:program.exe
Vala:通过 C 代码编译为原生代码作为中间代码
$ valac source1.vala source2.vala -o program
Vala 的标准对象系统是 GObject,编译后的 Vala 库是有效的 C 库。
使用包
单: -pkg:
$ gmcs source.cs -pkg:gtk-sharp-2.0 -out:program.exe
瓦拉: --pkg
$ valac source.vala --pkg gtk+-2.0 -o program
命名约定
C#
类、结构、委托类型: CamelCase
methods、properties、events 的 Actions: CamelCase
局部变量、字段: mixedCamelCase (有时 lower_case )
常量、枚举值: CamelCase
瓦拉
类、结构、委托类型: CamelCase
methods, properties, 属性, signals: lower_case
局部变量、字段: lower_case
常量、枚举值: UPPER_CASE
主要入口
C#: Main 必须位于类内
Vala: main ,可能在类之外
System Namespace
C#:最重要的命名空间是 System,默认情况下不导入
Vala:最重要的命名空间是 GLib,默认隐式导入
值类型
标准类型 ( int , long , ...) 的大小取决于架构
其他 Vala 类型 int8 、 、 int16 int32 、 int64 (有符号)、 uint8 、 uint16 、 uint32 uint64 (无符号) 具有独立于架构的保证大小
no byte , sbyte (改用 uint8 ) int8不 decimal
C# char 是 UCS-2,而不是 Vala 的 char ,但类似于 Vala 的 UCS-4 unichar
逐字字符串文本
C# 中: @"verbatim string"
瓦拉: """verbatim string"""
文档注释
C#:
/// <summary>
/// Documentation comment
/// </summary>
/// <param name="foo">...</param>
/// <param name="bar">...</param>
/// <returns>...</returns>
Vala:Valadoc 评论
/**
* Documentation comment
*
* @param foo ...
* @param bar ...
* @return ...
*/
Object 基类
C#:从 object (System.Object) 隐式继承
class Foo
{
// ...
}
Vala:没有来自 Object (GLib.Object) 的隐式继承
class Foo : Object {
// ...
}
如果您不继承 Object 会怎样 ?没什么可怕的。这些类将稍微轻量级一些,但是,它们将缺少一些功能,例如属性更改通知,并且您的对象将没有通用的基类。通常 inheriting from Object 是你想要的。
方法重载
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:没有方法重载,使用不同的名称或参数的默认值
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) {
}
}
多个构造函数
C#:构造函数重载
class Foo
{
public Foo() { }
public Foo(int foo) { }
public Foo(string bar) { }
}
new Foo();
new Foo(42);
new Foo("hello");
Vala:没有构造函数重载,而是命名构造函数
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");
构造函数链接
Base Constructor Chain-Up
C#
class Foo : Bar
{
public Foo() : base(42)
{
// ...
}
}
Vala:构造函数内的基调
class Foo : Bar {
public Foo () {
base (42);
// ...
}
}
多个构造函数链接
C#
class Foo
{
public Foo() : this("bar") { }
public Foo(string bar) { }
}
瓦拉
class Foo : Object {
public Foo () {
this.with_bar ("bar");
}
public Foo.with_bar (string bar) {
}
}
委托人 / Lambda
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:无类型的 lambda 表达式或直接方法赋值(无 new ... ),无 C# 2.0 样式
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); };
事件
C#:事件
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:信号
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);
}
}
无需额外的 delegate 声明,可以直接发出信号(无需 null 检查)。使用 .connect() and .disconnect() 而不是 += 和 -= 。但是 += ,在 Vala 中两者都是可能的,并且 -= 可能会被 signal connection 弃用。
信号不支持 add {} and remove {} 块。
接口
C#
interface IFoo
{
void Foo(int i);
int Bar(string s, double d);
}
Vala: public abstract 必需
interface Foo {
public abstract void foo (int i);
public abstract int bar (string s, double d);
}
为什么?因为 Vala 接口可能具有非抽象方法(即带有实现的方法)和 private methods!这意味着 Vala 接口可以用作 mixin(多重继承的受限形式)。
'I' 前缀在 GObject 世界中不常见,但允许
C#:接口继承
interface IfaceA
{
void MethodA();
}
interface IfaceB : IfaceA
{
void MethodB();
}
class Demo : IfaceB
{
public void MethodA() { }
public void MethodB() { }
}
Vala:接口先决条件
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 () { }
}
Vala 中的接口不能从其他接口继承,但它们可以将其他接口声明为先决条件,其工作方式大致相同。接口也可以将类作为先决条件。这通常用于确保接口的实例也是子 Object 类。接口不能从其他接口继承的事实主要只是一个技术区别——在实践中,Vala 的系统在这一领域的工作方式与 C# 相同,但具有先决条件类的额外功能。
枚举
Vala 枚举可能具有方法:
enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
public bool is_hot () {
return this == SUMMER;
}
}
在 C# 中,这只能通过扩展方法实现:
enum Season { Spring, Summer, Autumn, Winter }
static class SeasonExtensions
{
public static bool IsHot(this Season season)
{
return season == Season.Summer;
}
}
结构体初始化
C#
var p1 = new Point();
var p2 = new Point() { X = 2, Y = 3 };
Point p3;
p3.X = 2;
p3.Y = 3;
Vala:在不使用 new operator 的情况下实例化结构。
var p1 = Point ();
var p2 = Point () { x = 2, y = 3 };
Point p3 = { 2, 3 };
Vala 结构体必须在首次使用前进行初始化。Vala 结构无法实现接口。
多维数组
C#:矩形 [,] 和交错 [][] 多维数组
Vala:仅矩形 [,] 多维数组,计划支持交错数组
可为 null 的类型
C#:标记可为 null 的值类型
int? i = null;
Vala:标记可为 null 的引用类型参数并返回方法的值。默认情况下,它们是不可为空的!
Foo? method (Foo? foo, Bar bar) {
return null;
}
在此示例中: foo 且返回值可能是 null , bar 必须为非 null。在运行时检查,在某种程度上在编译时检查。
结论:相同的语法( ? 类型修饰符),不同的含义。
代码属性
C#:可自定义
Vala:内置于编译器中,主要用于 bindings 或 D-Bus 接口。bindings 最突出的属性是 [CCode (...)] .
性能
自动实现的属性的默认值
C#
class Person
{
public Person()
{
Name = "Default Name";
}
public string Name { get; set; }
}
瓦拉
class Person : Object {
public string name { get; set; default = "Default Name"; }
}
当然,在 constructor 中设置也可以。
属性更改通知
C#:实现 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 以通知信号
从 Object 派生的类的每个实例都有一个名为 notify 的信号。每次其对象的 property 发生更改时,都会发出此信号。
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";
}
如果您只对单个属性的更改通知感兴趣,则可以使用以下语法:
person.notify["name"].connect ((sender, property) => {
stdout.printf ("name has changed\n");
});
请注意,在这种情况下,您必须使用属性名称的字符串表示形式,其中下划线替换为破折号: my_property_name 在此表示形式中变为 "my-property-name" ,这是 GObject 属性命名约定。
在声明属性之前,可以使用 CCode 属性标记立即禁用更改通知:
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; }
}
异常
C#:未经检查的异常,基于类
[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:检查异常,Vala 术语:错误,不是基于类的,没有包装
// 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);
}
尽管编译器会针对忽略的错误发出警告,但它不会中止编译过程。这允许在没有适当错误处理的情况下进行原型设计,并有望防止忘记空 catch 块。
参数检查
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:引用类型参数被隐式检查, null 除非它们被 ? 标记为可空,因此您不必手动检查它们。方法可能具有前提条件:
void method (double d, int i, Foo foo)
requires (d >= 0.0 && d <= 1.0)
requires (i >= 0 && i <= 10)
{
// ...
}
Vala 还支持用于检查返回值的 postcontditions:
int square (int i)
ensures (result >= 0)
{
return i * i;
}
result 是表示返回值的特殊变量。
对可恢复的运行时错误(数据库错误、I/O 错误)使用异常(错误),对编程错误(如非法参数)使用前提条件和断言 ( assert (...) )。
不安全代码和指针
C#:必须在块内 unsafe { }
Vala:可以在任何地方使用。但是,仍然要小心。
条件编译指令
C#:编译器选项 -define: 或 -d:
Vala:编译器选项 --define 或 -D
没有 , #undef , , #region #endregion #line #define
资源处置
C#:析构函数是非确定性的,请改为实现 IDisposable,使用 using
class MyResource : IDisposable
{
public void Dispose()
{
// ...
}
}
/* Usage: */
using (var res = new MyResource()) {
// ...
}
Vala:析构函数是确定性的,您可以使用析构函数实现 RAII 模式
class MyResource : Object {
~MyResource () {
// ...
}
}
/* Usage: */
{
var res = new MyResource ();
// ...
}
一旦资源超出范围,就会立即释放资源。
内存管理
C#:垃圾回收
Vala:自动引用计数
这既有优点也有缺点。引用计数是确定性的,但在某些情况下可以形成引用循环。在这些情况下,您必须使用弱引用来打破这些循环。此的 Vala 关键字是 weak .
请参阅 Vala 的内存管理解释
异步调用
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:对异步方法 ( async , yield ) 的内置支持, --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 ();
}
}
静态构造函数
C#:在创建第一个实例或引用任何静态成员之前调用
class Foo
{
static Foo()
{
System.Console.WriteLine("Static constructor invoked.");
}
}
Vala: static construct { } 块。首次实例化类或其任何子类时,将运行此代码。可以保证此代码将在使用此类的程序中只运行一次。
class Foo : Object {
static construct {
stdout.printf ("Static constructor invoked.\n");
}
}
此外,Vala 类可以有一个 class construct { } 块。此块将在第一次使用其类时执行一次,并在第一次使用该类的每个子类时执行一次。
外部方法
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 属性,指定原始函数名称(仅当它与 Vala 名称不同时)
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));
}
}
使用 -X -l... 将库名称传递给编译器(其中 -X 表示下一个选项将传递给 C 编译器)
$ valac demo.vala -X -lfoo
您还可以将包含外部方法的 C 源文件传递给 Vala 编译器。这允许混合使用 Vala 和 C 源代码项目。
$ valac demo.vala foo.c
反射
Vala 中的反射支持是有限的。Vala 为其数据类型( is operator 、 foo.get_type () 、 dynamic casting via as operator)提供运行时类型信息。例如,您可以获取类型、信号和类以及枚举值的属性的名称。
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));
}
}
使用 GObjectIntrospection 支持构建的库可以完全内省。
不可用
没有 LINQ(不打算在 1.0 中,可能在以后)有一个 [https://gitlab.com/kosmospredanie/gpseq|实现]
无运算符重载 (vala-list)
没有方法或构造函数重载(如上所述,请改用不同的方法名称/命名构造函数)- 无扩展方法
对泛型类型参数没有约束(即 no where )- 无泛型委托
无转换运算符(即 no explicit 和 implicit )
没有 partial 类和方法
无 sealed 课程(计划)
没有静态类(请改用嵌套命名空间。Vala 支持命名空间方法,它们是隐式静态的)
否 goto , 无标记语句
无构造函数初始值设定项
数组的无边界检查(计划提供可选的支持)
否 , unchecked , , fixed stackalloc readonly checked
收集
C#:System.Collections.Generic 命名空间
Vala:Gee 命名空间、 --pkg gee-1.0 、 http://live.gnome.org/Libgee
粗略的等价物:
System.Collections.Generic |
哎呀 |
类 |
|
字典 |
哈希地图 |
哈希集 |
哈希集 |
链接列表 |
链接列表 |
列表 |
数组列表 |
队列 |
|
排序词典 |
树状图 |
叠 |
链接列表 |
接口 |
|
ICollection (英语) |
收集 |
IComparer |
可比较的 |
标识 |
地图 |
IEnumerable |
可迭代 |
IEnumerator |
迭 代 |
IList |
列表 |
|
队列 |
|
Deque (双队列) |
请参阅 Gee 示例
您可以通过索引器访问和分配 Gee 集合项(例如, my_map[key] 等效于 my_map.get (key) )。Vala 支持集合运算符 in : x in my_collection 等效于 my_collection.contains (x) .此运算符也适用于字符串,即使字符串不是集合。
请注意,Libgee 使用 . assert 检查索引键的边界等错误,因此它不会像在 C# 中那样引发任何可捕获对象 SystemException 。
索引
C#:使用 this 关键字定义索引器
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:实现 T get(int i) 和 void set(int i, T item) 方法
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、网络套接字
C#:System.IO、System.Net.Sockets 命名空间
Vala:GLib 命名空间(默认导入), --pkg gio-2.0 GIO 是 GLib 的一部分
控制台输入/输出
C#
System.Console.WriteLine("Hi!");
System.Console.Write("Please enter your name: ");
var name = System.Console.ReadLine();
System.Console.WriteLine("Welcome, {0}!", name);
瓦拉
stdout.printf ("Hi!\n");
stdout.printf ("Please enter your name: ");
var name = stdin.read_line ();
stdout.printf ("Welcome, %s!\n", name);
printf 使用与 C 函数相同的格式说明符。
GTK+ 演示应用程序
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();
}
}
瓦拉
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 的 GTK+ API 与原始的 GTK+ API 非常接近。事实上,你直接使用 GTK+ 函数,只是语法不同。
参见 GTK+ 示例
绑定
Mono:运行时绑定(包装器)、GAPI 工具、具有 XML 语法的 .sources 文件
Vala:无需运行时绑定,Vala 方法调用是直接的 C 函数调用,由 *.vapi (Vala API) 文件使用 Vala 语法映射,并带有 [CCode (...)]
在 Unix 系统上,VAPI 文件通常安装在
/usr/share/vala/vapi/ 或/usr/local/share/vala/vapi/
使用 vapi 文件(例如 foo-1.0.vapi):
$ valac source.vala --pkg foo-1.0
Vala 编译器还将查找具有相同基本名称(例如 foo-1.0.pc)的相应 pkg-config 文件 (*.pc),通常位于
/usr/lib/pkgconfig/ 或/usr/local/lib/pkgconfig/
并将其配置传递给 C 编译器(如果存在)。
VAPI 文件要么为 GObject 库自动生成,要么为非 GObject 库手写。