168 lines
4 KiB
Markdown
168 lines
4 KiB
Markdown
![]() |
# Dart Pure Reflection
|
||
|
|
||
|
A lightweight, cross-platform reflection system for Dart that provides runtime type introspection and manipulation without using `dart:mirrors` or code generation.
|
||
|
|
||
|
## Features
|
||
|
|
||
|
- ✅ Works on all platforms (Web, Mobile, Desktop)
|
||
|
- ✅ No dependency on `dart:mirrors`
|
||
|
- ✅ No code generation required
|
||
|
- ✅ Pure runtime reflection
|
||
|
- ✅ Type-safe property access
|
||
|
- ✅ Method invocation with argument validation
|
||
|
- ✅ Constructor invocation support
|
||
|
- ✅ Comprehensive error handling
|
||
|
|
||
|
## Installation
|
||
|
|
||
|
Add this to your package's `pubspec.yaml` file:
|
||
|
|
||
|
```yaml
|
||
|
dependencies:
|
||
|
reflection: ^1.0.0
|
||
|
```
|
||
|
|
||
|
## Usage
|
||
|
|
||
|
### Basic Setup
|
||
|
|
||
|
1. Add the `@reflectable` annotation and `Reflector` mixin to your class:
|
||
|
|
||
|
```dart
|
||
|
import 'package:reflection/reflection.dart';
|
||
|
|
||
|
@reflectable
|
||
|
class User with Reflector {
|
||
|
String name;
|
||
|
int age;
|
||
|
final String id;
|
||
|
|
||
|
User(this.name, this.age, {required this.id});
|
||
|
}
|
||
|
```
|
||
|
|
||
|
2. Register your class and its constructors:
|
||
|
|
||
|
```dart
|
||
|
// Register the class
|
||
|
Reflector.register(User);
|
||
|
|
||
|
// Register constructors
|
||
|
Reflector.registerConstructor(
|
||
|
User,
|
||
|
'', // Default constructor
|
||
|
(String name, int age, {String? id}) {
|
||
|
if (id == null) throw ArgumentError.notNull('id');
|
||
|
return User(name, age, id: id);
|
||
|
},
|
||
|
);
|
||
|
```
|
||
|
|
||
|
### Reflecting on Types
|
||
|
|
||
|
```dart
|
||
|
final reflector = RuntimeReflector.instance;
|
||
|
|
||
|
// Get type metadata
|
||
|
final userType = reflector.reflectType(User);
|
||
|
print('Type name: ${userType.name}');
|
||
|
print('Properties: ${userType.properties.keys.join(', ')}');
|
||
|
print('Methods: ${userType.methods.keys.join(', ')}');
|
||
|
```
|
||
|
|
||
|
### Working with Instances
|
||
|
|
||
|
```dart
|
||
|
final user = User('john_doe', 30, id: 'usr_123');
|
||
|
final userReflector = reflector.reflect(user);
|
||
|
|
||
|
// Read properties
|
||
|
final name = userReflector.getField('name'); // john_doe
|
||
|
final age = userReflector.getField('age'); // 30
|
||
|
|
||
|
// Write properties
|
||
|
userReflector.setField('name', 'jane_doe');
|
||
|
userReflector.setField('age', 25);
|
||
|
|
||
|
// Invoke methods
|
||
|
userReflector.invoke('someMethod', ['arg1', 'arg2']);
|
||
|
```
|
||
|
|
||
|
### Creating Instances
|
||
|
|
||
|
```dart
|
||
|
// Using default constructor
|
||
|
final newUser = reflector.createInstance(
|
||
|
User,
|
||
|
positionalArgs: ['alice', 28],
|
||
|
namedArgs: {'id': 'usr_456'},
|
||
|
) as User;
|
||
|
|
||
|
// Using named constructor
|
||
|
final specialUser = reflector.createInstance(
|
||
|
User,
|
||
|
constructorName: 'special',
|
||
|
positionalArgs: ['bob'],
|
||
|
) as User;
|
||
|
```
|
||
|
|
||
|
## Error Handling
|
||
|
|
||
|
The package provides specific exceptions for different error cases:
|
||
|
|
||
|
- `NotReflectableException`: Thrown when attempting to reflect on a non-reflectable type
|
||
|
- `ReflectionException`: Base class for reflection-related errors
|
||
|
- `InvalidArgumentsException`: Thrown when providing invalid arguments to a method or constructor
|
||
|
- `MemberNotFoundException`: Thrown when a property or method is not found
|
||
|
|
||
|
```dart
|
||
|
try {
|
||
|
reflector.reflect(NonReflectableClass());
|
||
|
} catch (e) {
|
||
|
print(e); // NotReflectableException: Type "NonReflectableClass" is not marked as @reflectable
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Complete Example
|
||
|
|
||
|
See the [example](example/reflection_example.dart) for a complete working demonstration.
|
||
|
|
||
|
## Limitations
|
||
|
|
||
|
1. Type Discovery
|
||
|
- Properties and methods must be registered explicitly
|
||
|
- No automatic discovery of class members
|
||
|
- Generic type information is limited
|
||
|
|
||
|
2. Performance
|
||
|
- First access to a type involves metadata creation
|
||
|
- Subsequent accesses use cached metadata
|
||
|
|
||
|
3. Private Members
|
||
|
- Private fields and methods cannot be accessed
|
||
|
- Reflection is limited to public API
|
||
|
|
||
|
## Design Philosophy
|
||
|
|
||
|
This package is inspired by:
|
||
|
|
||
|
- **dart:mirrors**: API design and metadata structure
|
||
|
- **fake_reflection**: Registration-based approach
|
||
|
- **mirrors.cc**: Runtime type handling
|
||
|
|
||
|
The goal is to provide a lightweight, cross-platform reflection system that:
|
||
|
|
||
|
- Works everywhere Dart runs
|
||
|
- Requires minimal setup
|
||
|
- Provides type-safe operations
|
||
|
- Maintains good performance
|
||
|
- Follows Dart best practices
|
||
|
|
||
|
## Contributing
|
||
|
|
||
|
Contributions are welcome! Please read our [contributing guidelines](CONTRIBUTING.md) before submitting pull requests.
|
||
|
|
||
|
## License
|
||
|
|
||
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|