Dart UnmodifiableMapBase
最后修改于 2025 年 4 月 4 日
在 Dart 中,UnmodifiableMapBase 是一个抽象基类,用于创建不可变 Map 视图。它提供对 Map 数据的只读访问,同时阻止修改。
UnmodifiableMapBase 实现 Map 接口,但会对所有修改操作抛出 UnsupportedError。它对于安全地公开 Map 数据而不允许更改非常有用。
UnmodifiableMapBase 基本用法
下面是如何通过继承 UnmodifiableMapBase 来创建一个简单的不可变 Map。
main.dart
import 'dart:collection';
class UnmodifiableMapView<K, V> extends UnmodifiableMapBase<K, V> {
final Map<K, V> _source;
UnmodifiableMapView(this._source);
@override
V? operator [](Object? key) => _source[key];
@override
Iterable<K> get keys => _source.keys;
}
void main() {
final source = {'a': 1, 'b': 2, 'c': 3};
final unmodifiable = UnmodifiableMapView(source);
print(unmodifiable['a']); // 1
print(unmodifiable.length); // 3
try {
unmodifiable['d'] = 4; // Throws
} catch (e) {
print('Error: $e');
}
}
我们创建一个 UnmodifiableMapView 类来包装一个源 Map。必须实现 [] 操作符和 keys getter。尝试修改将抛出异常。
$ dart main.dart 1 3 Error: Unsupported operation: Cannot modify unmodifiable map
使用工厂创建不可变 Map
一种更实用的方法是使用工厂方法来创建不可变 Map。
main.dart
import 'dart:collection';
class UnmodifiableMapFactory {
static Map<K, V> create<K, V>(Map<K, V> source) {
return _UnmodifiableMapWrapper(source);
}
}
class _UnmodifiableMapWrapper<K, V> extends UnmodifiableMapBase<K, V> {
final Map<K, V> _source;
_UnmodifiableMapWrapper(this._source);
@override
V? operator [](Object? key) => _source[key];
@override
Iterable<K> get keys => _source.keys;
}
void main() {
final source = {'x': 10, 'y': 20, 'z': 30};
final unmodifiable = UnmodifiableMapFactory.create(source);
print(unmodifiable['y']); // 20
print(unmodifiable.containsKey('x')); // true
try {
unmodifiable.clear(); // Throws
} catch (e) {
print('Error: $e');
}
}
此示例展示了创建不可变 Map 的工厂模式。私有的 _UnmodifiableMapWrapper 类实现了不可变行为。
$ dart main.dart 20 true Error: Unsupported operation: Cannot modify unmodifiable map
具有自定义键值类型的不可变 Map
UnmodifiableMapBase 可与自定义类型一起使用,同时保持不可变性。
main.dart
import 'dart:collection';
class Product {
final String id;
final String name;
Product(this.id, this.name);
@override
String toString() => 'Product($id, $name)';
}
class UnmodifiableProductMap extends UnmodifiableMapBase<String, Product> {
final Map<String, Product> _products;
UnmodifiableProductMap(this._products);
@override
Product? operator [](Object? key) => _products[key];
@override
Iterable<String> get keys => _products.keys;
}
void main() {
final products = {
'p1': Product('p1', 'Laptop'),
'p2': Product('p2', 'Phone'),
'p3': Product('p3', 'Tablet')
};
final unmodifiable = UnmodifiableProductMap(products);
print(unmodifiable['p2']); // Product(p2, Phone)
print(unmodifiable.values.where((p) => p.name.length > 5).toList());
try {
unmodifiable['p4'] = Product('p4', 'Monitor'); // Throws
} catch (e) {
print('Modification error: $e');
}
}
我们为 Product 对象创建了一个类型安全的不可变 Map。该 Map 提供只读访问,同时阻止对产品集合的任何修改。
$ dart main.dart Product(p2, Phone) [Product(p1, Laptop), Product(p3, Tablet)] Modification error: Unsupported operation: Cannot modify unmodifiable map
将 UnmodifiableMapBase 与其他集合结合使用
UnmodifiableMapBase 可以与其他集合类型结合使用,以创建复杂的不可变数据结构。
main.dart
import 'dart:collection';
class ImmutableConfig extends UnmodifiableMapBase<String, dynamic> {
final Map<String, dynamic> _config;
final List<String> _log;
ImmutableConfig(this._config, this._log);
@override
dynamic operator [](Object? key) {
_log.add('Accessed: $key');
return _config[key];
}
@override
Iterable<String> get keys => _config.keys;
}
void main() {
final config = {
'timeout': 30,
'retries': 3,
'debug': false
};
final accessLog = <String>[];
final immutableConfig = ImmutableConfig(config, accessLog);
print(immutableConfig['timeout']); // 30
print(immutableConfig['debug']); // false
try {
immutableConfig['timeout'] = 60; // Throws
} catch (e) {
print('Error: $e');
}
print('Access log: $accessLog');
}
此示例将一个不可变 Map 与一个日志列表结合使用。Map 保持不可变,同时跟踪访问尝试。日志列表可以单独访问。
$ dart main.dart 30 false Error: Unsupported operation: Cannot modify unmodifiable map Access log: [Accessed: timeout, Accessed: debug]
带懒加载的高级 UnmodifiableMapBase
我们可以通过重写访问器来实现不可变 Map 中的懒加载。
main.dart
import 'dart:collection';
class LazyUnmodifiableMap extends UnmodifiableMapBase<String, String> {
final Map<String, String> _source;
final Map<String, String> _cache = {};
LazyUnmodifiableMap(this._source);
@override
String? operator [](Object? key) {
if (key is! String) return null;
if (!_cache.containsKey(key)) {
print('Loading $key from source...');
_cache[key] = _source[key] ?? 'DEFAULT';
}
return _cache[key];
}
@override
Iterable<String> get keys => _source.keys;
}
void main() {
final source = {
'config1': 'Value1',
'config2': 'Value2',
'config3': 'Value3'
};
final lazyMap = LazyUnmodifiableMap(source);
print('First access:');
print(lazyMap['config2']); // Loads and caches
print('\nSecond access:');
print(lazyMap['config2']); // Uses cache
print('\nAll keys:');
print(lazyMap.keys.join(', '));
try {
lazyMap['new'] = 'value'; // Throws
} catch (e) {
print('\nError: $e');
}
}
这个高级示例使用缓存实现了懒加载。值在第一次访问时加载,并在后续请求中缓存。Map 保持不可变。
$ dart main.dart First access: Loading config2 from source... Value2 Second access: Value2 All keys: config1, config2, config3 Error: Unsupported operation: Cannot modify unmodifiable map
最佳实践
- 不可变设计: 用于安全地公开内部状态。
- 性能: 考虑对昂贵的查找进行缓存。
- 类型安全: 为强类型创建特定子类。
- 文档: 清晰地记录不可变性。
- 组合: 与工厂等其他模式结合。
来源
本教程通过实际示例介绍了 Dart 的 UnmodifiableMapBase,演示了不可变 Map 的实现和模式。
作者
列出 所有 Dart 教程。