data_class's People
data_class's Issues
Fix mixin type
Suggestion: Optimize generated code
Hi again,
I wanted to bring up a suggestion that this library could generate code that is more optimized. Currently it relies on iterating over props, which is slower than "hand written" equals or hashcode. It's not "slow" because it takes probably micro or nanoseconds, but its relatively slower than what you would write manually. Here's a sample benchmark:
test.dart
import 'package:mek_data_class/mek_data_class.dart';
part 'gen/test.g.dart';
@DataClass()
class TestDataClass with _$TestDataClass {
final String name;
final int age;
TestDataClass(this.name, this.age);
}
class TestManualClass {
final String name;
final int age;
TestManualClass(this.name, this.age);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is TestManualClass &&
runtimeType == other.runtimeType &&
name == other.name &&
age == other.age;
@override
int get hashCode => name.hashCode ^ age.hashCode;
}
const warmupIterations = 10000;
const iterations = 10000000;
void main() {
test('TestManualClass', TestManualClass('John', 30), TestManualClass('Jack', 31));
test('TestDataClass', TestDataClass('John', 30), TestDataClass('Jack', 31));
}
void test(String name, Object a, Object b) {
for (var i = 0; i < warmupIterations; i++) {
a == b;
}
final stopwatch = Stopwatch()..start();
for (var i = 0; i < iterations; i++) {
a == b;
}
stopwatch.stop();
print("$name equals: ${stopwatch.elapsedMilliseconds}");
stopwatch.reset();
for (var i = 0; i < warmupIterations; i++) {
a.hashCode;
}
stopwatch.start();
for (var i = 0; i < iterations; i++) {
a.hashCode;
}
stopwatch.stop();
print("$name hashCode: ${stopwatch.elapsedMilliseconds}");
}
On my machine, i9 9900K, I get the following:
TestManualClass equals: 65
TestManualClass hashCode: 48
TestDataClass equals: 1061
TestDataClass hashCode: 751
It also may have a slight impact on garbage collection as it needs to allocate iterators, but I didn't measure that.
So seeing as this is a codegen lib, I think this is exactly what a codegen lib should do - generate the boring boilerplate. I understand why it currently generates what it does - it's a simpler implementation. But since it already has access to all the fields, it can generate something that resembles the "manual" version more.
Curious about your thoughts on this. And thanks for this lib regardless :)
Allow setting fields to `null` in `copyWith`
Right now, it is not possible to pass null
to copyWith
to set a nullable field to null
, instead we have to use the Changes
class.
However, it would be possible to include this functionality by generating a bit of extra code.
Here is an idea as to what this could look like:
final Object _undefined = Object();
/// not nullable property
final String a;
/// nullable property
final String? b;
MyClass copyWith({
String? a,
String? b,
}) => _copyWith(
a: a,
b: b,
);
MyClass _copyWith({
Object? a = _undefined,
Object? b = _undefined,
}) => MyClass(
/// because this property cant be [null], both [_undefined] and [null] count as absent
a: a == _undefined || a == null ? this.a : a,
/// because this property can be [null], only [_undefined] counts as passing nothing.
b: b == _undefined ? this.b : b,
);
To demonstrate that this code actually works as intended, we can use this example:
Source Code
class MyClass {
MyClass({
required this.a,
required this.b,
});
static const Object _undefined = Object();
/// not nullable property
final String a;
/// nullable property
final String? b;
MyClass copyWith({
String? a,
String? b,
}) =>
_copyWith(
a: a,
b: b,
);
MyClass _copyWith({
Object? a = _undefined,
Object? b = _undefined,
}) =>
MyClass(
/// because this property cant be [null], both [_undefined] and [null] count as absent
a: a == _undefined || a == null ? this.a : a as String,
/// because this property can be [null], only [_undefined] counts as passing nothing.
b: b == _undefined ? this.b : b as String?,
);
@override
String toString() {
return 'MyClass(a: $a, b: $b)';
}
}
void main() {
MyClass c = MyClass(
a: 'a',
b: 'b',
);
print(c);
c = c.copyWith(b: null);
print(c);
}
Unnecessary use of parentheses (linter warning)
On generated mixins toString()
:
@override
String toString() => (ClassToString('AuthenticationEvent')).toString();
Should be
@override
String toString() => ClassToString('AuthenticationEvent').toString();
Incorrect code generated for data classes with positional parameters
Hi, thanks for this package!
When trying to use it I noticed that it doesn't support classes with positional parameters and expects all parameters to be named.
@DataClass()
class Test with _$Test {
final int positional1;
final String positional2;
final bool named1;
const Test(this.positional1, this.positional2, {this.named1 = true});
}
generates
mixin _$Test {
Test get _self => this as Test;
Iterable<Object?> get _props sync* {
yield _self.positional1;
yield _self.positional2;
yield _self.named1;
}
bool operator ==(Object other) =>
identical(this, other) ||
other is Test &&
runtimeType == other.runtimeType &&
DataClass.$equals(_props, other._props);
int get hashCode => Object.hashAll(_props);
String toString() => (ClassToString('Test')
..add('positional1', _self.positional1)
..add('positional2', _self.positional2)
..add('named1', _self.named1))
.toString();
Test copyWith({
int? positional1,
String? positional2,
bool? named1,
}) {
return Test(
positional1: positional1 ?? _self.positional1,
positional2: positional2 ?? _self.positional2,
named1: named1 ?? _self.named1,
);
}
Test change(void Function(_TestChanges c) updates) =>
(_TestChanges._(_self)..update(updates)).build();
_TestChanges toChanges() => _TestChanges._(_self);
}
class _TestChanges {
int positional1;
String positional2;
bool named1;
_TestChanges._(Test dc)
: positional1 = dc.positional1,
positional2 = dc.positional2,
named1 = dc.named1;
void update(void Function(_TestChanges c) updates) => updates(this);
Test build() => Test(
positional1: positional1,
positional2: positional2,
named1: named1,
);
}
which contains a copyWith
method and a _TestChanges
class that assume all parameters are named.
I have a simple use case where the first 2 parameters are obvious and the rest of the parameters are named and optional, and I don't wish to make the first 2 parameters named.
Suggestion: add simple serialization methods
It would be nice if we could serialize and deserialize those classes to a Map<String, Object>
.
Nothing fancy, only drop the values inside the map and vice-versa. No conversions, no renaming, nothing.
So,
@DataClass()
class Product with _$Product {
final String title;
@DataField(equality: DefaultEquality())
final double price;
const Product({
required this.title,
required this.price,
});
String get titlePrice => '$title$price';
}
outputs
// This is a Map, not a JSON string!
<String, Object>{
"title": "some title",
"price": 3.14,
}
And that's it! The dev would be responsible to make this map encodable by, let's say, JSON (then, there, it will handle incompatible types, such as DateTime
, Enum
, etc.)
Suggestion: Consider making field class static
In most cases there is no need for a path inside Fields class.
To use a field from a class currently you have to create a new class of fields ProfileFields().username
, while you could use ProfileFields.username
. I expect that when static meta programming is available I would personally use a static version or even an enum and the switch is going to be easier if that's the case here too.
The current implementation with a prefix seems a bit arbitrary.
Make ClassToString print null values
Thank you really for this simple yet useful package, I love that is preserves dart class models compared to other data class generators. What is missing, is that I don't see class null values. I suppose void add(String field, Object? value) { if (value == null) return;
is the one that causing it. Null is still a value, otherwise it seems like I don't have such parameter at all. You don't need to force it, just give an option to switch on or off for each generated ClassToString
, idk in build.yaml file for example.
Thank you!
Changes class not generating while using sealed times
@immutable
@DataClass()
abstract base class Event with _$Event {
const Event();
}
@immutable
@DataClass()
sealed class AuthenticationEvent extends Event with _$AuthenticationEvent {
const AuthenticationEvent();
}
@immutable
@DataClass()
final class LoginEvent extends AuthenticationEvent with _$LoginEvent {
const LoginEvent({required this.loginInfo});
final LoginInfo loginInfo;
}
@immutable
@DataClass()
final class LogoutEvent extends AuthenticationEvent with _$LogoutEvent {
const LogoutEvent();
}
On generated code: Classes and mixins can only implement other classes and mixins
class _LoginEventChanges implements _AuthenticationEventChanges {
...
}
That _AuthenticationEventChanges
doesn't exist.
DataField annotation not respects updatable option
typedef, namespace support
Generator not respects namespaces nor aliases. The latter is not so important, but missing namespace support causes compile error.
import 'package:some_package/api.dart' as api;
typedef MyCustomDate = DateTime
@DataClass()
class Product with _$Product {
final api.Product apiProduct;
final MyCustomDate date;
}
generates:
Product copyWith({
Product? apiProduct,
DateTime? date,
})
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.