[英]How to call a named constructor from a generic function in Dart/Flutter
我希望能够从通用 function 内部构造一个 object。我尝试了以下操作:
abstract class Interface
{
Interface.func(int x);
}
class Test implements Interface
{
Test.func(int x){}
}
T make<T extends Interface>(int x)
{
// the next line doesn't work
return T.func(x);
}
但是,这不起作用。 我收到以下错误消息: The method 'func' isn't defined for the class 'Type'
。
注意:我不能使用镜像,因为我正在使用 dart 和 flutter。
Dart不支持从泛型类型参数实例化。 想要使用命名构造函数还是默认构造函数都没关系( T()
也不起作用)。
可能有一种在服务器上执行此操作的方法,在该服务器上可以使用dart:mirrors
(反射)(尚未尝试过),但在Flutter或浏览器中无法使用。
您需要维护类型到工厂功能的映射
void main() async {
final double abc = 1.4;
int x = abc.toInt();
print(int.tryParse(abc.toString().split('.')[1]));
// int y = abc - x;
final t = make<Test>(5);
print(t);
}
abstract class Interface {
Interface.func(int x);
}
class Test implements Interface {
Test.func(int x) {}
}
/// Add factory functions for every Type and every constructor you want to make available to `make`
final factories = <Type, Function>{Test: (int x) => Test.func(x)};
T make<T extends Interface>(int x) {
return factories[T](x);
}
Günter Zöchbauer 的优秀答案的变体和使用 Dart 2.15 中新的构造函数撕裂功能可能是向泛型函数添加一个额外的参数以提供构造函数(如果需要,使用Test.new
作为默认构造函数)。
T make2<T extends Interface>(int x, T Function(int) constructor)
{
return constructor(x);
}
make2<Test>(5, Test.func);
// Default constructor
make<Test>(5, Test.new);
T Function(int) constructor
参数是一个函数,它接受一个int
参数并返回类型T
。
您也可以在不使用构造函数撕下的情况下完成此操作,但是(至少对我而言)这有点难以解析。
make<Test>(5, (int x) => Test.func(x));
这也有一个有用的属性,如果开发人员在不更改参数的情况下复制和粘贴函数,则应该通过静态分析来提醒他们(因为 AnotherTest 不返回 Test),而不是仅在运行时比较工厂列表时才发现到类型。
// ERROR: The argument type 'AnotherTest Function(int)' can't be
// assigned to the parameter type 'Test Function(int)'
make2<AnotherTest>(5, Test.func);
我还没有看到任何好的(完整/具体/非深奥)解决方法示例:所以这是我的:
使用如下:
void main() {
final user = Model.fromJson<User>({
"id": 1,
"username": "bobuser",
"name": "Bob",
"email": "bob@example.com",
});
print(user.runtimeType);
print(user is User); // Unnecessary type check; the result is always 'true'.
print(user.toJson());
}
dart lib/main.dart
User
true
{id: 1, username: bobuser, name: Bob, email: bob@example.com}
abstract class Model {
/// It's really a shame that in 2022 you can't do something like this:
// factory Model.fromJson<T extends Model>(Map<String, dynamic> json) {
// return T.fromJson(json);
// }
/// Or even declare an abstract factory that must be implemented:
// factory Model.fromJson(Map<String, dynamic> json);
// Not DRY, but this works.
static T fromJson<T extends Model>(Map<String, dynamic> json) {
switch (T) {
case User:
/// Why the heck without `as T`, does Dart complain:
/// "A value of type 'User' can't be returned from the method 'fromJson' because it has a return type of 'T'."
/// when clearly `User extends Model` and `T extends Model`?
return User.fromJson(json) as T;
case Todo:
return Todo.fromJson(json) as T;
case Post:
return Post.fromJson(json) as T;
default:
throw UnimplementedError();
}
}
Map<String, dynamic> toJson();
}
class User implements Model {
User({
required this.id,
required this.username,
required this.name,
required this.email,
});
final int id;
final String username;
final String name;
final String email;
factory User.fromJson(Map<String, dynamic> json) => User(
id: json["id"],
username: json["username"],
name: json["name"],
email: json["email"],
);
@override
Map<String, dynamic> toJson() => {
"id": id,
"username": username,
"name": name,
"email": email,
};
}
class Todo implements Model {
Todo({
required this.id,
required this.userId,
required this.title,
required this.completed,
});
final int id;
final int userId;
final String title;
final bool completed;
factory Todo.fromJson(Map<String, dynamic> json) => Todo(
id: json["id"],
userId: json["userId"],
title: json["title"],
completed: json["completed"],
);
@override
Map<String, dynamic> toJson() => {
"id": id,
"userId": userId,
"title": title,
"completed": completed,
};
}
class Post implements Model {
Post({
required this.id,
required this.userId,
required this.title,
required this.body,
});
final int id;
final int userId;
final String title;
final String body;
factory Post.fromJson(Map<String, dynamic> json) => Post(
id: json["id"],
userId: json["userId"],
title: json["title"],
body: json["body"],
);
@override
Map<String, dynamic> toJson() => {
"id": id,
"userId": userId,
"title": title,
"body": body,
};
}
因此,截至 2022 年底,我发现最简洁的方法是使用一个中间值 class,它将一个可分离的构造函数作为参数并从中派生出类型。
通用事件示例:
这是我为只允许传递字符串的进程间通信所做的。
事件是类型化的,但在发送时自动转换为字符串并在接收时解析回对象。
梦想
var OnResized = new MessageEvent<Sized>("resized");
现实
var OnResized = new MessageEvent("resized", Size.fromJson);
它没有我想要的那么干净,但最终它只是多了 8 个字符。
用法
OnResized+=SizeChanged;
void onSizeChanged(Size size) {
}
OnResized.Invoke(new Size(399,400));
这是存储工厂得到回报的地方。 class 的用户对内部工作完全无视,根本不用担心打字。
执行
typedef T CreateFromJson<T extends ISerializable>(Map<String, dynamic> json);
typedef void MessageHandler<T>(T args);
class MessageEvent<T extends ISerializable> {
String Name;
CreateFromJson<T> ReseultParser;
void _notify(String data) {
var result = ReseultParser(json.decode(data));
for (var callback in _callbacks) {
callback.call(result);
}
}
void Invoke(T data) {
_sendMessage(Name, json.encode(data.toJson()));
}
MessageEvent(this.Name, this.ReseultParser) {
_addEventListener(Name, this);
}
final List<MessageHandler<T>> _callbacks = <MessageHandler<T>>[];
void Add(MessageHandler<T> callback) {
_callbacks.add(callback);
}
MessageEvent<T> operator +(MessageHandler<T> callback) {
Add(callback);
return this;
}
static void OnMessage(MessageParams message, dynamic other) {
_listeners[message.type]?._notify(message.params);
}
static _sendMessage(String type, String args) {
SendMessageProtocall?.call(MessageParams(type: type, params: args));
}
static SendProtocall? SendMessageProtocall;
static final _listeners = <String, MessageEvent>{};
static void _addEventListener(String type, MessageEvent event) {
_listeners[type] = event;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.