这是用户在 2024-10-19 10:56 为 https://wiki.gnome.org/Projects/Vala/ValaForCSharpProgrammers 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?

 面向 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,默认隐式导入

 值类型


  • 标准类型 ( intlong , ...) 的大小取决于架构


  • 其他 Vala 类型 int8 、 、 int16 int32int64 (有符号)、 uint8uint16uint32 uint64 (无符号) 具有独立于架构的保证大小


  • no bytesbyte (改用 uint8int8

  •  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#:从 objectSystem.Object) 隐式继承

class Foo
{
    // ...
}


Vala:没有来自 ObjectGLib.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 且返回值可能是 nullbar 必须为非 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:对异步方法 ( asyncyield ) 的内置支持, --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 explicitimplicit


  • 没有 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

 哎呀

 

 字典

 哈希地图

 哈希集

 哈希集

 链接列表

 链接列表

 列表

 数组列表

 队列


LinkedList、PriorityQueue

 排序词典

 树状图

 

 链接列表

 接口

 ICollection (英语)

 收集

IComparer

 可比较的

 标识

 地图

IEnumerable

 可迭代

IEnumerator

 迭 代

IList

 列表

 队列

 Deque (双队列)


请参阅 Gee 示例


您可以通过索引器访问和分配 Gee 集合项(例如, my_map[key] 等效于 my_map.get (key) )。Vala 支持集合运算符 inx 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.IOSystem.Net.Sockets 命名空间


Vala:GLib 命名空间(默认导入), --pkg gio-2.0 GIO 是 GLib 的一部分


请参阅 GIO 示例GIO 网络示例

 控制台输入/输出

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 库写。


Projects/Vala/ValaForCSharpProgrammers (最后编辑于 2019-08-13 00:23:28 by Gavr Maxutenko