D有反射吗?
Does D have reflection?
D 是否有反射或任何类似的东西,以便能够在运行时访问对象?
如果没有:
我将如何在运行时访问或编辑对象?
例如:
bool myFunc(string status){
switch(status){
case "create":
Object my_obj = new createObject();
write("Object has been created or whatever");
break;
case "mutate":
//Don't want to have to make a global declaration
my_obj.attribute = "A meme";
write("Object has been mutated or whatever");
break;
case "delete":
//Don't want to have to make a global declaration
delete();
write("Object has been deleted or whatever");
break;
default:
//blah
break;
}
return true;
}
void main(){
while(true)
{String status = readln();myFunc(status);}
}
目前我能想到的就这些了,请告诉我关于这个话题我对D的误解。
我查看了 dlang.org 上的文档,并没有真正找到与反射有关的东西,或者至少没有找到 Java 的方式。
ps,上面的代码是pseudo-code,我只是当场编的,我敢肯定不管什么原因它不会真正编译,我只是希望为了解决这个问题,我想以一种特定的方式访问对象,这样对我来说很方便。
是的,D有反射,但是没有,不像Java。
D 的反射以编译时构建块的形式出现,而不是运行时方法。当然,您可以自己创建运行时方法,但它不会开箱即用。
实际上我今天刚写了一个东西,反射循环一个方法来显示它的属性并让你编辑它:https://twitter.com/adamdruppe/status/1066390612516179968 It is not done yet but I'll link so you can see some of it in action: https://github.com/adamdruppe/arsd/commit/5411dd9844869909466f2091391abe091b529bf8#diff-7a88942576365ca3d7f389b766344477R6587
无论如何,我的方法是使用反射信息从简单的循环中创建方法。该语言提供了两个功能,__traits,以及执行此操作的 is 表达式:
https://dlang.org/spec/traits.html
https://dlang.org/spec/expression.html#IsExpression
并且标准库使用 std.traits 模块包装和扩展
http://dpldocs.info/experimental-docs/std.traits.html
(或者如果你更喜欢基本相同文档的官方网站,只是我更难read/navigate:https://dlang.org/phobos/std_traits.html)
您可以将其与其他代码生成技术(如模板混入)和传统技术(如接口和构造函数)相结合,以创建运行时内容。
但对于简单的情况,请尝试这样的操作:
import std.stdio;
import std.conv;
import std.traits;
class MyClass {
void myMethod() {}
int anotherMethod(int a) { return a; }
// this is the runtime bridge function. The trick here is to do
// a switch, just like your example, but the innards are auto-generated
// from the compile time reflection.
string call(string methodName, string[] args) {
// it starts as a plain switch...
method_switch: switch(methodName) {
// but inside, we use a static foreach - a compile-time loop -
// over the __traits(allMembers) magic, which gives a list of all member names
static foreach(inspecting; __traits(allMembers, typeof(this))) {
case inspecting: { // you can create switch cases inside these static loops
// for this example, I only want to use callable methods, so this
// static if - a compile time if statement - will filter out anything else.
//
// It is possible to do more, like plain data members, child classes, and more,
// but that will add a lot more code. Same basic ideas with each of them though.
static if(isCallable!(__traits(getMember, this, inspecting))) {
// after we confirm it is callable, we can get a delegate of it
// (same as normally doing `&member.method`) to call later.
auto callable = &__traits(getMember, this, inspecting);
// next is building the argument list. Parameters comes from the std.traits
// module in the standard library and gives an object representing the function's
// parameters. We can loop over these and set them!
Parameters!callable arguments;
foreach(i, ref arg; arguments) { // ref loop cuz we setting the arg members..
// so for the runtime bridge here, I took everything as strings and
// want to convert them to the actual method arguments. In many cases,
// that automatic conversion will not be possible. The next bit of magic,
// __traits(compiles), will take some code and return true if it successfully
// compiles. Using the static if block, I can turn what would be compile time
// errors into a runtime exception instead.
static if(__traits(compiles, to!(typeof(arg))(args[i])))
// note that to is from the stdlib again: std.conv. It converts
// a thing from one type to another. Here, I ask it to convert our
// string (args is the runtime array of strings the user passed) to
// whatever the type is that the method expects.
//
// Note that this might throw an exception if the string is wrong.
// For example, passing "lol" to a method expecting an int will throw
// an exception saying cannot convert string "lol" to int.
arg = to!(typeof(arg))(args[i]);
else
// or if the conversion didn't compile at all, it will always throw.
throw new Exception("method " ~ methodName ~ " not callable with this reflection code because of incompatible argument type");
}
// now that we have the arguments, time to tackle the return value.
// the main special case here is a void return - that is not a value
// and thus cannot be converted. So we do it separately.
// Otherwise, though, inside we just call our callable from above with
// the arguments from above and return the value converted to string!
static if(is(ReturnType!callable == void)) {
// it returned void, so call it and return null
// because the language won't let us return void
// directly as a string nor convert it easily.
callable(arguments);
return null;
} else {
// it returned something else, just call the function
// and convert it to a string
return to!string(callable(arguments));
}
}
} break method_switch;
}
default:
throw new Exception("no such method " ~ methodName);
}
assert(0); // not reached
}
}
// and let's call them with some strings. You could also get these strings from
// the user (just btw remember to .strip them if they come from readln, otherwise
// the trailing newline character will cause a method not found exception.)
void main() {
auto obj = new MyClass();
writeln(obj.call("myMethod", []));
writeln(obj.call("anotherMethod", ["5"]));
}
这可能有助于 copy/paste 从网站上编写代码并阅读常规编辑器中的评论,因为 Stack Overflow 经常会让您滚动,这很难。我在评论中展示了基本的想法。
一旦您编写了一次反射桥函数并使其以您满意的方式工作......您可以添加任意数量的方法,它就会工作!
事实上,您甚至可以使 call
方法成为接口的一部分,并定义 mixin template
的主体部分(参见 https://dlang.org/spec/template-mixin.html )并将其弹出进入您的任何 classes.
class MyNewClass : Reflectable {
mixin CallImplementation;
}
// it will now work there!
接口允许您从基础 classes 引用它(与 Java 大部分相同),混合模板允许您将反射提供者复制到每个子 class,所以它甚至在子 classes 上也给出了所有方法。 Java 会自动为你做那件事,但在 D 中你确实需要为每个人添加那条混合线。不是太麻烦,而是需要考虑的事情。 (实际上 D 也有可能自动执行此操作......但它需要破解核心运行时库,因此这是一个相当高级的主题并且仅在特殊情况下有用(因为你必须在整个项目范围内使用那个被破解的库)。所以可能对你没用,只是暗示它在那里。)
顺便说一下,使用接口,您还可以向 classes 添加一个静态构造函数,以将它们注册到一些运行时关联数组或开关或工厂函数的任何 class 名称中,并创建他们也来自字符串。没有特别特殊的代码可以实现这一点,它与您以前可能见过的模式相同,但如果您需要来自字符串的新 class 对象而不是仅仅编辑现有对象,这就是我开始的方式。
我会把这些细节留给你玩,让我知道这里是否有任何意义。
D 编程语言支持编译时反射,但不支持运行 时反射(例如Java)。有关编译时反射的更多详细信息,请参阅 Adam 的回答。
10 多年前,Thomas Kühne 写了一个很棒的包,叫做 FlectioneD (http://dsource.org/projects/flectioned),它仍然是这个主题的一个很好的参考...
D 是否有反射或任何类似的东西,以便能够在运行时访问对象?
如果没有:
我将如何在运行时访问或编辑对象?
例如:
bool myFunc(string status){
switch(status){
case "create":
Object my_obj = new createObject();
write("Object has been created or whatever");
break;
case "mutate":
//Don't want to have to make a global declaration
my_obj.attribute = "A meme";
write("Object has been mutated or whatever");
break;
case "delete":
//Don't want to have to make a global declaration
delete();
write("Object has been deleted or whatever");
break;
default:
//blah
break;
}
return true;
}
void main(){
while(true)
{String status = readln();myFunc(status);}
}
目前我能想到的就这些了,请告诉我关于这个话题我对D的误解。
我查看了 dlang.org 上的文档,并没有真正找到与反射有关的东西,或者至少没有找到 Java 的方式。
ps,上面的代码是pseudo-code,我只是当场编的,我敢肯定不管什么原因它不会真正编译,我只是希望为了解决这个问题,我想以一种特定的方式访问对象,这样对我来说很方便。
是的,D有反射,但是没有,不像Java。
D 的反射以编译时构建块的形式出现,而不是运行时方法。当然,您可以自己创建运行时方法,但它不会开箱即用。
实际上我今天刚写了一个东西,反射循环一个方法来显示它的属性并让你编辑它:https://twitter.com/adamdruppe/status/1066390612516179968 It is not done yet but I'll link so you can see some of it in action: https://github.com/adamdruppe/arsd/commit/5411dd9844869909466f2091391abe091b529bf8#diff-7a88942576365ca3d7f389b766344477R6587
无论如何,我的方法是使用反射信息从简单的循环中创建方法。该语言提供了两个功能,__traits,以及执行此操作的 is 表达式:
https://dlang.org/spec/traits.html
https://dlang.org/spec/expression.html#IsExpression
并且标准库使用 std.traits 模块包装和扩展
http://dpldocs.info/experimental-docs/std.traits.html
(或者如果你更喜欢基本相同文档的官方网站,只是我更难read/navigate:https://dlang.org/phobos/std_traits.html)
您可以将其与其他代码生成技术(如模板混入)和传统技术(如接口和构造函数)相结合,以创建运行时内容。
但对于简单的情况,请尝试这样的操作:
import std.stdio;
import std.conv;
import std.traits;
class MyClass {
void myMethod() {}
int anotherMethod(int a) { return a; }
// this is the runtime bridge function. The trick here is to do
// a switch, just like your example, but the innards are auto-generated
// from the compile time reflection.
string call(string methodName, string[] args) {
// it starts as a plain switch...
method_switch: switch(methodName) {
// but inside, we use a static foreach - a compile-time loop -
// over the __traits(allMembers) magic, which gives a list of all member names
static foreach(inspecting; __traits(allMembers, typeof(this))) {
case inspecting: { // you can create switch cases inside these static loops
// for this example, I only want to use callable methods, so this
// static if - a compile time if statement - will filter out anything else.
//
// It is possible to do more, like plain data members, child classes, and more,
// but that will add a lot more code. Same basic ideas with each of them though.
static if(isCallable!(__traits(getMember, this, inspecting))) {
// after we confirm it is callable, we can get a delegate of it
// (same as normally doing `&member.method`) to call later.
auto callable = &__traits(getMember, this, inspecting);
// next is building the argument list. Parameters comes from the std.traits
// module in the standard library and gives an object representing the function's
// parameters. We can loop over these and set them!
Parameters!callable arguments;
foreach(i, ref arg; arguments) { // ref loop cuz we setting the arg members..
// so for the runtime bridge here, I took everything as strings and
// want to convert them to the actual method arguments. In many cases,
// that automatic conversion will not be possible. The next bit of magic,
// __traits(compiles), will take some code and return true if it successfully
// compiles. Using the static if block, I can turn what would be compile time
// errors into a runtime exception instead.
static if(__traits(compiles, to!(typeof(arg))(args[i])))
// note that to is from the stdlib again: std.conv. It converts
// a thing from one type to another. Here, I ask it to convert our
// string (args is the runtime array of strings the user passed) to
// whatever the type is that the method expects.
//
// Note that this might throw an exception if the string is wrong.
// For example, passing "lol" to a method expecting an int will throw
// an exception saying cannot convert string "lol" to int.
arg = to!(typeof(arg))(args[i]);
else
// or if the conversion didn't compile at all, it will always throw.
throw new Exception("method " ~ methodName ~ " not callable with this reflection code because of incompatible argument type");
}
// now that we have the arguments, time to tackle the return value.
// the main special case here is a void return - that is not a value
// and thus cannot be converted. So we do it separately.
// Otherwise, though, inside we just call our callable from above with
// the arguments from above and return the value converted to string!
static if(is(ReturnType!callable == void)) {
// it returned void, so call it and return null
// because the language won't let us return void
// directly as a string nor convert it easily.
callable(arguments);
return null;
} else {
// it returned something else, just call the function
// and convert it to a string
return to!string(callable(arguments));
}
}
} break method_switch;
}
default:
throw new Exception("no such method " ~ methodName);
}
assert(0); // not reached
}
}
// and let's call them with some strings. You could also get these strings from
// the user (just btw remember to .strip them if they come from readln, otherwise
// the trailing newline character will cause a method not found exception.)
void main() {
auto obj = new MyClass();
writeln(obj.call("myMethod", []));
writeln(obj.call("anotherMethod", ["5"]));
}
这可能有助于 copy/paste 从网站上编写代码并阅读常规编辑器中的评论,因为 Stack Overflow 经常会让您滚动,这很难。我在评论中展示了基本的想法。
一旦您编写了一次反射桥函数并使其以您满意的方式工作......您可以添加任意数量的方法,它就会工作!
事实上,您甚至可以使 call
方法成为接口的一部分,并定义 mixin template
的主体部分(参见 https://dlang.org/spec/template-mixin.html )并将其弹出进入您的任何 classes.
class MyNewClass : Reflectable {
mixin CallImplementation;
}
// it will now work there!
接口允许您从基础 classes 引用它(与 Java 大部分相同),混合模板允许您将反射提供者复制到每个子 class,所以它甚至在子 classes 上也给出了所有方法。 Java 会自动为你做那件事,但在 D 中你确实需要为每个人添加那条混合线。不是太麻烦,而是需要考虑的事情。 (实际上 D 也有可能自动执行此操作......但它需要破解核心运行时库,因此这是一个相当高级的主题并且仅在特殊情况下有用(因为你必须在整个项目范围内使用那个被破解的库)。所以可能对你没用,只是暗示它在那里。)
顺便说一下,使用接口,您还可以向 classes 添加一个静态构造函数,以将它们注册到一些运行时关联数组或开关或工厂函数的任何 class 名称中,并创建他们也来自字符串。没有特别特殊的代码可以实现这一点,它与您以前可能见过的模式相同,但如果您需要来自字符串的新 class 对象而不是仅仅编辑现有对象,这就是我开始的方式。
我会把这些细节留给你玩,让我知道这里是否有任何意义。
D 编程语言支持编译时反射,但不支持运行 时反射(例如Java)。有关编译时反射的更多详细信息,请参阅 Adam 的回答。
10 多年前,Thomas Kühne 写了一个很棒的包,叫做 FlectioneD (http://dsource.org/projects/flectioned),它仍然是这个主题的一个很好的参考...