Как на самом деле работает конструктор const?
Я заметил, что в Dart можно создать конструктор const. В документации сказано, что const
Слово используется для обозначения чего-то постоянного времени компиляции.
Мне было интересно, что происходит, когда я использую const
конструктор для создания объекта. Это как неизменный объект, который всегда одинаков и доступен во время компиляции? Как выглядит концепция const
конструктор на самом деле работает? Чем константный конструктор отличается от "обычного" конструктора?
6 ответов
Конструктор Const создает "канонизированный" экземпляр.
То есть все константные выражения начинают канонизироваться, а затем эти "канонизированные" символы используются для распознавания эквивалентности этих констант.
Канонизация:
Процесс преобразования данных, имеющих более одного возможного представления, в "стандартное" каноническое представление. Это может быть сделано для сравнения различных представлений на эквивалентность, для подсчета количества различных структур данных, для повышения эффективности различных алгоритмов за счет исключения повторяющихся вычислений или для обеспечения возможности наложения значимого порядка сортировки.
Это означает, что выражения const, такие как const Foo(1, 1)
может представлять любую пригодную для использования форму, которая полезна для сравнения на виртуальной машине.
Виртуальная машина должна принимать во внимание только тип значения и аргументы в том порядке, в котором они встречаются в этом выражении const. И, конечно же, они сокращены для оптимизации.
Константы с одинаковыми канонизированными значениями:
var foo1 = const Foo(1, 1); // #Foo#int#1#int#1
var foo2 = const Foo(1, 1); // #Foo#int#1#int#1
Константы с разными канонизированными значениями (поскольку подписи различаются):
var foo3 = const Foo(1, 2); // $Foo$int$1$int$2
var foo4 = const Foo(1, 3); // $Foo$int$1$int$3
var baz1 = const Baz(const Foo(1, 1), "hello"); // $Baz$Foo$int$1$int$1$String$hello
var baz2 = const Baz(const Foo(1, 1), "hello"); // $Baz$Foo$int$1$int$1$String$hello
Константы не воссоздаются каждый раз. Они канонизируются во время компиляции и хранятся в специальных справочных таблицах (где они хешируются своими каноническими сигнатурами), из которых они впоследствии используются повторно.
PS
Форма #Foo#int#1#int#1
используемый в этих примерах используется только для целей сравнения и не является реальной формой канонизации (представления) в Dart VM;
Но настоящей формой канонизации должно быть "стандартное" каноническое представление.
Я нашел ответ Лассе в блоге Криса Шторма отличным объяснением.
Я надеюсь, что они не против, что я копирую контент.
Это хорошее объяснение конечных полей, но оно не объясняет конструкторы const. Ничто в этих примерах на самом деле не использует, что конструкторы являются константными конструкторами. Любой класс может иметь конечные поля, константные конструкторы или нет.
Поле в Dart на самом деле является анонимным местом хранения в сочетании с автоматически созданным средством получения и установки, которое считывает и обновляет хранилище, а также может быть инициализировано в списке инициализатора конструктора.
Окончательное поле такое же, только без установщика, поэтому единственный способ установить его значение находится в списке инициализатора конструктора, и после этого невозможно изменить значение - отсюда и "финал".
Смысл конструкторов const не в том, чтобы инициализировать конечные поля, это может сделать любой генерирующий конструктор. Задача состоит в том, чтобы создать постоянные значения времени компиляции: объекты, в которых все значения полей известны уже во время компиляции, без выполнения каких-либо операторов.
Это накладывает некоторые ограничения на класс и конструктор. Константный конструктор не может иметь тела (никакие операторы не выполняются!), А его класс не должен иметь никаких неокончательных полей (значение, которое мы "знаем" во время компиляции, не должно быть в состоянии изменить позже). Список инициализатора должен также инициализировать поля только для других констант времени компиляции, поэтому правые части ограничены "константными выражениями времени компиляции"[1]. И перед ним должен стоять префикс "const" - в противном случае вы просто получите обычный конструктор, который соответствует этим требованиям. Это прекрасно, просто это не константный конструктор.
Чтобы использовать конструктор const для фактического создания объекта константы во время компиляции, вы затем заменяете "new" на "const" в выражении "new". Вы все еще можете использовать "new" с const-конструктором, и он все равно будет создавать объект, но это будет просто новый новый объект, а не значение константы во время компиляции. То есть: константный конструктор также может использоваться как обычный конструктор для создания объектов во время выполнения, а также для создания постоянных объектов во время компиляции во время компиляции.
Итак, в качестве примера:
class Point { static final Point ORIGIN = const Point(0, 0); final int x; final int y; const Point(this.x, this.y); Point.clone(Point other): x = other.x, y = other.y; //[2] } main() { // Assign compile-time constant to p0. Point p0 = Point.ORIGIN; // Create new point using const constructor. Point p1 = new Point(0, 0); // Create new point using non-const constructor. Point p2 = new Point.clone(p0); // Assign (the same) compile-time constant to p3. Point p3 = const Point(0, 0); print(identical(p0, p1)); // false print(identical(p0, p2)); // false print(identical(p0, p3)); // true! }
Константы времени компиляции канонизируются. Это означает, что независимо от того, сколько раз вы пишете "const Point(0,0)", вы создаете только один объект. Это может быть полезно - но не так сильно, как может показаться, поскольку вы можете просто создать переменную const для хранения значения и использовать ее вместо этого.
Итак, чем же хороши константы времени компиляции?
- Они полезны для перечислений.
- Вы можете использовать постоянные значения времени компиляции в случаях переключения.
- Они используются в качестве аннотаций.
Константы времени компиляции были важнее, прежде чем Дарт переключился на ленивую инициализацию переменных. До этого вы могли объявить только инициализированную глобальную переменную, такую как "var x = foo;" если "foo" была константой времени компиляции. Без этого требования большинство программ могут быть написаны без использования каких-либо const-объектов.
Итак, краткое резюме: Const-конструкторы предназначены только для создания значений констант во время компиляции.
/ L
[1] Или действительно: "Потенциально постоянные выражения времени компиляции", потому что это может также ссылаться на параметры конструктора. [2] Так что да, класс может иметь как константные, так и неконстантные конструкторы одновременно.
Очень хорошо объяснено в деталях, но для пользователей, которые на самом деле ищут использование конструктора const
Он используется для увеличения производительности Flutter, поскольку помогает Flutter перестраивать только те виджеты, которые должны быть обновлены. При использовании setState() в StateFulWidgets будут перестроены только те компоненты, которые не являются конструктором const
Можно пояснить на примере->
class _MyWidgetState extends State<MyWidget> {
String title = "Title";
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(
children: <Widget>[
const Text("Text 1"),
const Padding(
padding: const EdgeInsets.all(8.0),
child: const Text("Another Text widget"),
),
const Text("Text 3"),
],
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
setState(() => title = 'New Title');
},
),
);
}
}
Так как в этом примере должен быть изменен только текстовый заголовок, поэтому только этот виджет должен быть перестроен, поэтому создание всех других виджетов в качестве конструктора const поможет флаттер сделать то же самое для повышения производительности.
в этом видео Вы узнаете, зачем нам это нужно. https://www.youtube.com/watch?v=B1fIqdqwWw8&amp;t=558s С 09:18 до 16:09
В документации: https://dart.dev/guides/language/language-tour
постоянные конструкторы. Чтобы создать константу времени компиляции с помощью конструктора констант, поместите ключевое слово const перед именем конструктора:
> var p = const ImmutablePoint(2, 2);
Создание двух идентичных констант времени компиляции приводит к единственному каноническому экземпляру:
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1,1);
assert(identical(a, b)); // They are the same instance!
В постоянном контексте вы можете опустить константу перед конструктором или литералом. Например, посмотрите на этот код, который создает карту констант:
// Lots of const keywords here.
const pointAndLine = const {
'point': const [const ImmutablePoint(0, 0)],
'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
Вы можете опустить все, кроме первого использования ключевого слова const:
// Only one const, which establishes the constant context.
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)], };
Если константный конструктор находится вне константного контекста и вызывается без константы, он создает непостоянный объект:
> var a = const ImmutablePoint(1, 1); // Creates a constant var b =
> ImmutablePoint(1, 1); // Does NOT create a constant
>
> assert(!identical(a, b));// NOT the same instance!
Для получения дополнительной информации вы можете проверить эти 2 ответа ниже:
1- /questions/44186756/kak-na-samom-dele-rabotaet-konstruktor-const/44186773#44186773
2- /questions/44186756/kak-na-samom-dele-rabotaet-konstruktor-const/44186764#44186764
Пример демо, который const instance действительно решает по финальному полю. И в этом случае это не может быть предсказано во время компиляции.
import 'dart:async';
class Foo {
final int i;
final int j = new DateTime.now().millisecond;
const Foo(i) : this.i = i ~/ 10;
toString() => "Foo($i, $j)";
}
void main() {
var f2 = const Foo(2);
var f3 = const Foo(3);
print("f2 == f3 : ${f2 == f3}"); // true
print("f2 : $f2"); // f2 : Foo(0, 598)
print("f3 : $f3"); // f3 : Foo(0, 598)
new Future.value().then((_) {
var f2i = const Foo(2);
print("f2 == f2i : ${f2 == f2i}"); // false
print("f2i : $f2i"); // f2i : Foo(0, 608)
});
}
Теперь дартс проверит это.
Анализ дротиков:
[dart] Невозможно определить конструктор const, так как поле 'j' инициализировано с непостоянным значением
Ошибка выполнения:
/main.dart ': ошибка: строка 5 поз 17: выражение не является допустимой константой времени компиляции final int j = new DateTime.now().millisecond;
Все популярные ответы используются, чтобы знать все о const и о том, как их использовать.
Что, если вы хотите найти все возможные коды, чтобы сделать их константными в своем проекте?
Для этого можно использовать пакет lint !
На основе документации lint:
lint - это тщательно подобранный сборник правил lint с открытым исходным кодом для проектов Dart и Flutter, созданный сообществом. Набор правил соответствует « Эффективному дротику: руководство по стилю» .