Почему и какие три конструктора валы?Вала разных типов конструкторов
- класс построить
- построить
- метод с именем класса не
и более конкретно, почему третья конструкция не вызывается при использовании его из файла Gtk.Builder?
Почему и какие три конструктора валы?Вала разных типов конструкторов
и более конкретно, почему третья конструкция не вызывается при использовании его из файла Gtk.Builder?
Короткий ответ: потому что это работает GObject. Длинный ответ - это всего лишь кусочек дольше:
C не имеет объектов или конструкторов, он имеет структуры и функции. Структуры очень просты; они содержат поля, и все. Нет методов, конструкторов, destuctors и т.д. Они выглядят следующим образом:
typedef struct Foo_ {
int bar;
char* baz;
} Foo;
К «Instantiate» на структуру, то выделить необходимую память (либо в стеке или в куче, я сосредоточусь на куче для остальная часть вопроса) и установите поля.
Это быстро становится проблемой даже для нашей простой структуры, поэтому вы обычно видите функции, которые помогут в распределении и освобождении структур. Что-то вроде этого:
Foo* foo_new (int bar, char* baz) {
Foo* foo = malloc (sizeof (Foo));
/* malloc() can fail. Some libraries would return null, some would
just assume it never does. GLib-based software generally just exits
with an error, which is what we'll do here. */
if (NULL == foo) {
fprintf (stderr, "%s:%d: Unable to allocate room for struct Foo\n",
__FILE__, __LINE__);
exit (EXIT_FAILURE);
}
foo->bar = bar;
foo->baz = (NULL != baz) ? strdup (baz) : NULL;
return foo;
}
void foo_free (Foo* foo) {
if (NULL == foo)
return;
if (NULL != foo->baz)
free (foo->baz);
free (foo);
}
В Vala в *_new
функций отображаются именованными конструкторы. Вала связывания для этого может выглядеть примерно так:
[Compact]
public class Foo {
public Foo();
public int bar;
public string? baz;
}
Это все довольно просто, но то, что происходит, когда вы хотите, чтобы «расширить» Foo
и добавить новое поле? C не поддерживает языковую поддержку для «расширения» структуры. Программисты обойти эту проблему путем внедрения базовой структуры в ребенке структуре:
typedef struct Qux_ {
struct Foo parent;
int quux;
} Qux;
Это довольно приличное решение на уровне C; первая часть QUX структуры точно так же, как Foo структуры, поэтому, когда мы хотим использовать Qux
как Foo
все, что мы должны сделать, это бросить:
void qux_set_bar_and_qux (Qux* qux, int bar, int quux) {
((Foo*) qux)->bar = bar;
qux->quux = quux;
}
К сожалению, ломается довольно плохо при создании новый экземпляр. Помните, что наша функция foo_new
выделяет кусок sizeof(Foo)
байтов на кучу (с использованием malloc
) - место для всего поля quux
! Это означает, что мы не можем назвать нашу функцию foo_new
.
Если вы звоните в библиотеку, написанную в Вала, есть способ обойти это: помимо функции foo_new
, Vala фактически сгенерирует функцию foo_construct
.Таким образом, учитывая то, как
[Compact]
public class Foo {
public Foo (int bar, string? baz) {
this.bar = bar;
this.baz = baz;
}
}
Что Вал будет фактически генерировать что-то немного, как это:
void foo_construct (Foo* foo, int bar, char* baz) {
foo->bar = bar;
foo->baz = g_strdup (baz);
}
Foo* foo_new (int bar, char* baz) {
Foo* foo = g_malloc (sizeof (Foo));
foo_construct (foo, bar, baz);
return foo;
}
Теперь, если наш Qux
класса в Vala подклассов Foo
, он может позвонить в наших Foo
имени конструктора:
[Compact]
public class Qux : Foo {
public Qux (int quux) {
base (0, "Hello, world");
this.quux = quux;
}
public int quux;
}
Поскольку сгенерированный код не на самом деле назвать foo_new
, он вызывает foo_construct
:
Qux* qux_new (int quux) {
Qux* qux = g_malloc (sizeof (Qux));
foo_construct ((Foo*) qux, 0, "Hello, world");
qux->quux = quux;
}
К сожалению, код не написан в Vala редко следует этой конвенции (Grep для '' has_construct_function атрибута CCODE в VAPIs распределенных с valac).
На этом этапе вы можете подумать: «Это боль, но почему бы просто не воссоздать содержимое функции foo_new в qux_new». Хорошо, потому что у вас может не быть доступа к содержимому структуры Foo
. Человек, который написал Foo
, может не хотеть, чтобы вы возились со своими частными полями, чтобы они могли сделать Foo
неполным типом в публичных заголовках и сохранить полное определение для себя.
Теперь давайте начнем говорить о GObject properties. Я собираюсь немного рассказать о деталях, но в основном это позволяет вам регистрировать типы и включает в себя немного информации о них, которая доступна во время выполнения.
Занятия, зарегистрированные в GObject, могут иметь свойства. Они концептуально несколько похожи на поля в C-структурах, но тип обеспечивает обратные вызовы для их загрузки и хранения вместо того, чтобы просто оставить ваш код в адресе напрямую. Это также означает, что он может думать, как излучать сигнал, когда вы устанавливаете значение, и некоторые другие удобные вещи.
Инициализация классов в GObject довольно сложна. Мы поговорим об этом чуть больше минуты, но сначала посмотрим на это с точки зрения библиотеки, которая хочет создать экземпляр класса GObject. Это будет выглядеть примерно так:
Qux* qux = g_object_new (QUX_TYPE,
"bar", 1729,
"baz", "Hello, world",
"quux", 1701,
NULL);
Это, вероятно, довольно очевидно, что это делает: он создает Qux
экземпляр, и устанавливает свойство «бар» в 1729, «Баз» на «Привет, мир», и " quux "до 1701. Теперь вернемся к , как экземпляр класса. Опять же, это (более чем) немного упрощена, но ...
Во-первых, достаточно памяти для хранения Qux
экземпляра (в том числе Foo
родительского класса, а теперь также GObject
класс, который является предком всех GObjects) выделяется ,
Затем вызываются обратные вызовы для установки свойств «bar», «baz» и «qux».
Далее вызывается функция *_constructor
. В Vala это сопоставляется с блоком construct
. Это выглядит примерно так:
static GObject * foo_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam construct_properties[n_construct_properties]) {
GObjectClass * parent_class = G_OBJECT_CLASS (foo_parent_class);
GObject * obj = parent_class->constructor (type, n_construct_properties, construct_properties);
Foo * self = G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_FOO, Foo);
/* The code from your construct block goes here */
return obj;
}
Обратите внимание, что вы не можете контролировать аргументы этой функции. Как вы можете видеть, каждый конструктор вызывает конструктор своего родительского класса.Я добавил комментарий, где идет код из вашего блока построения; мы вернемся к тому, что скоро будет отделено от именованного конструктора.
Теперь давайте посмотрим на код для именованного конструктора. Помните, что подавляющее большинство библиотек не имеет *_construct
функцию, поэтому мы будем представлять себе тот, который не делает (для нашего Qux
класса):
Qux* qux_new (int bar, int quux) {
Qux* qux = g_object_new (QUX_TYPE,
"bar", bar,
"quux", quux,
NULL);
/* Code from your named constructor goes here. */
}
В конце концов, мы получаем, почему ваше имя конструктора ISN 't вызывается при использовании GtkBuilder
: он не вызывает qux_new
, он вызывает g_object_new
. Calling qux_new
is an enormous pain без знания вашей библиотеки, и, очевидно, это невозможно для GtkBuilder
, чтобы узнать о вашей библиотеке.
Наконец, давайте поговорим о блоках конструкций класса. В основном это совсем другое дело. К счастью, для их объяснения не требуется почти столько времени: они вызывается, когда тип зарегистрирован в GObject, не, когда экземпляр экземпляра создается. По сути, он получает вызов в первый раз, когда ваш экземпляр создается, и никогда больше. Быстрый пример:
public class Foo : GLib.Object {
class construct {
GLib.message ("Hello, world!");
}
construct {
GLib.message ("%d", this.bar);
}
public int bar { get; set; default = 1729; }
}
private static int main (string[] args) {
var foo = new Foo();
foo = new Foo();
return 0;
}
Выведет
Hello, world!
1729
1729