gpt4 book ai didi

How to call a named constructor from a generic function in Dart/Flutter(如何在DART/FILTH中从泛型函数调用命名构造函数)

转载 作者:bug小助手 更新时间:2023-10-25 09:59:14 28 4
gpt4 key购买 nike



I want to be able to construct an object from inside a generic function. I tried the following:

我希望能够从泛型函数内部构造一个对象。我尝试了以下方法:



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);
}


However, this doesn't work. And I get the following error message: The method 'func' isn't defined for the class 'Type'.

然而,这并不管用。我得到了以下错误消息:没有为类‘Type’定义方法‘func’。



Note: I cannot use mirrors because I'm using dart with flutter.

注意:我不能用镜子,因为我用的是扑翼飞镖。


更多回答
优秀答案推荐

Dart does not support instantiating from a generic type parameter. It doesn't matter if you want to use a named or default constructor (T() also does not work).

DART不支持从泛型类型参数实例化。无论您想要使用命名构造函数还是默认构造函数(T()也不起作用)。



There is probably a way to do that on the server, where dart:mirrors (reflection) is available (not tried myself yet), but not in Flutter or the browser.

可能有一种方法可以在服务器上做到这一点,在服务器上可以使用DART:Mirrors(反射)(我还没有尝试过),但不能在Fighter或浏览器中使用。



You would need to maintain a map of types to factory functions

您需要维护类型到工厂函数的映射



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);
}


A variation of Günter Zöchbauer's excellent answer and using the new constructor tearoff feature in Dart 2.15 could be to add an extra parameter to the generic function to provide the constructor (using Test.new for a default constructor, if required).

Günter Zöchbauer出色的回答和使用DART 2.15中的新构造函数tearoff特性的一个变体可以是向泛型函数添加额外的参数以提供构造函数(如果需要,使用Test.new作为默认构造函数)。


T make2<T extends Interface>(int x, T Function(int) constructor)
{
return constructor(x);
}

make2<Test>(5, Test.func);

// Default constructor
make2<Test>(5, Test.new);

The T Function(int) constructor parameter is a Function which takes an int argument and returns Type T.

T函数(Int)构造函数参数是一个接受int参数并返回类型T的函数。


You could have done this without constructor tear-offs too, but (for me at least) this is just a little too much to parse.

您也可以在不删除构造函数的情况下完成此操作,但(至少对我而言)这有点太难解析了。


make2<Test>(5, (int x) => Test.func(x));

This also has the useful attribute that if a developer copies and pastes the function without changing the parameter, they should be alerted by static analysis (since AnotherTest.func doesn't return Test), rather than only finding out at runtime when the factory list is compared to the types.

这还具有一个有用的属性,即如果开发人员在不更改参数的情况下复制和粘贴函数,应该通过静态分析提醒他们(因为AnotherTest.func不返回测试),而不是只在运行时发现工厂列表与类型进行了比较。


// ERROR: The argument type 'AnotherTest Function(int)' can't be 
// assigned to the parameter type 'Test Function(int)'
make2<Test>(5, AnotherTest.func);


I haven't seen any good (complete/concrete/non-esoteric!) workaround examples, so here's mine:

我没有看到任何好的(完全的/具体的/非深奥的!)解决方法示例,下面是我的:


Use as follows:

使用方法如下:


main.dart


void main() {
final user = Model.fromJson<User>({
"id": 1,
"username": "bobuser",
"name": "Bob",
"email": "[email protected]",
});
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: [email protected]}

models.dart


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,
};
}


So as of late 2022 the cleanest way I've found to do this is to use an intermediate class that takes a tear away constructor as a parameter and derives the type from it.

因此,到2022年底,我发现的最简单的方法是使用一个中间类,它接受一个拆分的构造函数作为参数,并从它派生类型。


Example for Generic Events:

一般事件的示例:


This is something I made for inter process communication that only allowed passing strings.

这是我为只允许传递字符串的进程间通信而设计的。


The events are typed, but automatically converted into a string on send and parsed back into objects on receive.

事件是类型化的,但在发送时自动转换为字符串,并在接收时解析回对象。


The Dream

梦中之梦


var OnResized = new MessageEvent<Sized>("resized");

The Reality

现实


var OnResized = new MessageEvent("resized", Size.fromJson);

It's not as clean as I would like, but ultimately it's only an extra 8 characters.

它不像我希望的那样干净,但最终只有8个额外的字符。


Usage

用法


OnResized+=SizeChanged;

void onSizeChanged(Size size) {

}
OnResized.Invoke(new Size(399,400));

This is where storing the factory pays off. The users of the class are completely oblivious to the inner workings and don't have to worry about typing at all.

这就是储存工厂的回报所在。这个类的用户完全不知道内部的工作原理,根本不必担心打字。


Implementation

实施


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;
}
}

更多回答

It a shame that it's not possible because that would've been super useful.

遗憾的是,这是不可能的,因为这将是超级有用的。

I wonder if there is a technical limitation to adding this feature to dart. I don't actually know.

我想知道在DART中添加此功能是否有技术限制。其实我也不知道。

It's mostly about whether the amount of work and added complexity is worth it. The Dart team is working on making Dart better and this is something that comes up frequently. I guess they'll address it eventually, but currently it seem other features that have better cost/value ratio are being worked on.

这主要是关于工作量和增加的复杂性是否值得。DART团队正在努力使DART变得更好,这是经常出现的事情。我猜他们最终会解决这个问题,但目前看来,其他性价比更高的功能正在开发中。

Thanks, that's a really useful workaround.

谢谢,这是一个非常有用的变通办法。

Funny how there's the Flutter Fetch data from the internet tutorial showing the fromJson method, and then various threads on failed attempts on calling this on a generic type. I don't get why <T implements X> does not guarantee that T has e.g. a fromJson declared by X (T.fromJson(...)).

有趣的是,从互联网教程中提取数据的过程显示了FromJson方法,然后各种线程尝试在泛型类型上调用该方法,但都失败了。我不明白为什么不能保证T有一个由X声明的from Json(T.from Json(...))。

I'm revisiting my question to see if a different answer should be marked as accepted. I found your answer to be the best solution for my case: I can pass any function, the interface is no longer needed, and can be simplified to this: make(5, Test.func) which is amazing. I'm marking your answer as accepted even though it is not an exact solution for the way I happened to frame the question (calling a constructor from a generic function) because it is very good solution.

我正在重新考虑我的问题,看看是否应该将不同的答案标记为已接受。我发现你的答案对我来说是最好的解决方案:我可以传递任何函数,不再需要接口,可以简化为:make(5,Test.func),这太棒了。我将您的答案标记为已接受,尽管这不是我构建问题的确切解决方案(从泛型函数调用构造函数),因为它是非常好的解决方案。

That's exactly the use case that I asked the question for. I believe that everyone using dart will have this use case because whether they are writing a mobile or web app, will have to write a deserializer for data coming from the server or the database.

这正是我提出问题的用例。我相信每个使用DART的人都会有这个用例,因为无论他们是编写移动应用程序还是Web应用程序,都必须为来自服务器或数据库的数据编写反序列化程序。

Thank you for this clear and concrete workaround! This was exactly what I was looking for as well. However, I only needed a generic way of calling fromJson() on many objects and was able to easily take it out the Model interface from your example and it still worked great

感谢您提供的这一清晰具体的解决方法!这也正是我正在寻找的。然而,我只需要一种在许多对象上调用from Json()的通用方法,并且能够轻松地将其从您的示例中的Model接口中删除,而且它仍然工作得很好

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com