Not sure if the terminology in the title is 100% correct, but what I mean is easily illustrated by this example:
class MyClass{
String str = '';
MyClass(this.str);
}
void main() {
MyClass obj1 = MyClass('obj1 initial');
print(obj1.str);
doSomething(obj1);
print(obj1.str);
doSomethingElse(obj1);
print(obj1.str);
}
void doSomething(MyClass obj){
obj.str = 'obj1 new string';
}
void doSomethingElse(MyClass obj){
obj = MyClass('obj1 new object');
}
This will print
obj1 initial
obj1 new string
obj1 new string
But what if I wanted doSomethingElse() to actually modify what obj1 is referencing, so that the output would be:
obj1 initial
obj1 new string
obj1 new object
Is this possible in Dart, and if so, how?
No, Dart does not pass arguments by reference. (Without something like C++'s complex type system and rules, it's not clear how it would work if the caller didn't bind the argument to a variable.)
You instead could add a level of indirection (i.e., by putting obj1 inside another object, such as a List, Map, or your own class). Another possibility would be to make doSomethingElse a nested function, and then it could directly access and modify variables in the enclosing scope.
You have a reference problem in that function,
When you call doSomethingElse(obj1) in main your,
MyObject obj parameter referencing the obj1 value,
then obj you're referencing the MyClass('obj1 new objcet'),
and you're not changing the obj1 reference in the main
void doSomethingElse(MyClass obj){ // let's say we gave the parameter obj1
// here obj referencing the obj1 value
obj = MyClass('obj1 new object');
//and then it is referencing the MyClass('obj1 new object') value
//nothing change for obj1 it still referencing the same value
}
You can return that object and give reference to that object like this,
class MyClass {
String str = '';
MyClass(this.str);
}
void main() {
MyClass obj1 = MyClass('obj1 initial');
print(obj1.str);
doSomething(obj1);
print(obj1.str);
obj1 = doSomethingElse();
print(obj1.str);
}
void doSomething(MyClass obj) {
obj.str = 'obj1 new string';
}
MyClass doSomethingElse() {
return MyClass('obj1 new object');
}
output :
Related
I'm writing a testing utility function and would like to pass a class member name and get its value. Could I use Symbols for this? My test code looks like this
class Foo {
String a = 'A';
String b = 'B';
static void output(Symbol symbol) {
debugPrint("The value is '$symbol'");
}
}
Foo.output(#a);
I'm trying to get a result like The value is 'A' but I'm getting The value is 'Symbol("a")'?
Getting the value of something by name, where the name is a value, and at runtime, that is reflection.
You need dart:mirrors for that, which isn't available on most platforms.
Better yet, don't do it at all. Dart has first class functions, so you can pass in a function accessing the variable instead:
class Foo {
String a = 'A';
String b = 'B';
static void output(String read(Foo value)) {
debugPrint("The value is '${read(this)}'");
}
}
void main() {
var foo = Foo();
foo.output((f) => f.a);
foo.output((f) => f.b);
}
I am passing a Function as an optional parameter to the constructor but I can't assign a default value.
void main() {
Person p = Person();
print(p.foo('Hello'));
}
class Person {
final String Function(String) foo;
Person({this.foo});
}
now trying to assign a default value: Person({this.foo = (val) {return val;});
produces the error: Error: Not a constant expression. I am aware the parameter must be const but using const or even static infront of (val) {return val;} does not work.
Does anyone have an idea how to solve this problem?
You can try this:
void main() {
Person pLower = Person(foo: (a) => a.toLowerCase());
print(pLower.foo('Hello'));
Person pDefault = Person();
print(pDefault.foo('Hello'));
}
class Person {
static String defaultFoo(String a) => a.toUpperCase();
final String Function(String) foo;
Person({this.foo = defaultFoo});
}
Output
hello
HELLO
You can only use constant values (aka. compile-time constants) as default values.
You cannot create a constant function literal, so there is no way to write the function in-line in the constructor.
However, references to top-level or static functions are constants, so you can declare the default value function as a static function or top-level function.
void main() {
Person p = Person();
print(p.foo('Hello')); // Prints "Hello"
}
class Person {
final String Function(String) foo;
Person({this.foo = _identity});
static String _identity(String value) => value;
}
// or as top-level.
// String _identity(String value) => value;
You can (and should) choose to make the function public if the default value is on an instance method, and you expect anyone to extend or implement your class. In that case, they need to declare the same default value.
Another option, which is often at least as useful, is to not use a default value, but replace a null before using the value:
class Person {
final String Function(String) foo;
Person({String Function(String) foo}) : foo = foo ?? _identity;
static String _identity(String value) => value;
}
or even using a non-constant value:
class Person {
final String Function(String) foo;
Person({String Function(String) foo}) : foo = (foo ?? (String x) => x);
}
For a constructor, it makes very little difference. If it was an instance method instead, using ?? to replace null avoids subclasses having to use the exact same function as default value.
Personally I recommend always using ?? instead of a default value. It's more flexible since it allows non-constant values. For non-function default values, you'll have to document the default behavior instead of just letting the dartDoc show {int x = 42}, but for functions, you'll have to document them anyway.
Why the List list = new List() execute before the constructor HomeContent?
class HomeContent extends StatelessWidget{
List list = new List(); // no matter I write this line code before or after `HomeContent()`
HomeContent() {
for(var i = 0; i < 21; i ++) {
this.list.add("I am item $i .");
}
}
Widget build(BuildContext context) {
return ListView.builder(
itemCount: this.list.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(this.list[index]),
);
}
);
}
}
The order in which you add class members has no effect on the order of execution. When constructing a new object, the class's properties are initialised before the constructor is executed.
Dart object initialization is defined to work the way you observe.
When you write new Foo(...) then the language allocates space for a Foo object and then goes through the initialization process for the Foo class and all its superclasses.
The initialization of an instance of Foo happens as follow:
First evaluate instance field initializers declared in Foo in source order.
Then evaluate the initializer list of the Foo class.
Then recursively initialize the instance as Foo's superclass, as specified by the super invocation of the initializer list (defaults to super() if you write nothing).
When done with that, execute the Foo constructor body.
When this process hits the Object class, the object is considered properly initialized, and it's made available to the constructor bodies that are executed on the way back.
As an example:
Object log(Object value) {
print(value);
return value;
}
class Super /* extends Object */ {
var superField6 = log(6);
var superField9;
Super(this.superField5) : superField8 = log(8), superField9 = log(9) {
log(10);
}
var superField7 = log(7);
var superField8;
var superField5;
}
class Sub extends Super {
var subField1 = log(1);
var subField4;
Sub() : subField3 = log(3), subField4 = log(4), super(log(5)) {
log(11);
}
var subField2 = log(2);
var subField3;
}
main() {
new Sub();
}
If you run this code, you will see the log calls happening in the order of their arguments.
Dart has a concept of final. Most dynamic languages don't have this concept.
What is final and what do I use it for?
final variables can contain any value, but once assigned, a final variable can't be reassigned to any other value.
For example:
main() {
final msg = 'hello';
msg = 'not allowed'; // **ERROR**, program won't compile
}
final can also be used for instance variables in an object. A final field of a class must be set before the constructor body is run. A final field will not have an implicit setter created for it, because you can't set a new value on a final variable.
class Point {
final num x, y;
Point(this.x, this.y);
}
main() {
var p = new Point(1, 1);
print(p.x); // 1
p.x = 2; // WARNING, no such method
}
It's important to realize that final affects the variable, but not the object pointed to by the variable. That is, final doesn't make the variable's object immutable.
For example:
class Address {
String city;
String state;
Address(this.city, this.state);
}
main() {
final address = new Address("anytown", "hi");
address.city = 'waikiki';
print(address.city); // waikiki
}
In the above example, the address variable is marked as final, so it will always point to the object instantiated by the new Address("anytown", "hi") constructor. However, the object itself has state that is mutable, so it's perfectly valid to change the city. The only thing prevented by final is reassigning the address variable.
Because my userdata objects reference themselves, I need to delete and nil a variable for the garbage collector to work.
Lua code:
obj = object:new()
--
-- Some time later
obj:delete() -- Removes the self reference
obj = nil -- Ready for collection
C Code:
typedef struct {
int self; // Reference to the object
int callback; // Reference to a Lua function
// Other members and function references removed
} Object;
// Called from Lua to create a new object
static int object_new( lua_State *L ) {
Object *obj = lua_newuserdata( L, sizeof( Object ) );
// Create the 'self' reference, userdata is on the stack top
obj->self = luaL_ref( L, LUA_REGISTRYINDEX );
// Put the userdata back on the stack before returning
lua_rawgeti( L, LUA_REGISTRYINDEX, obj->self );
// The object pointer is also stored outside of Lua for processing in C
return 1;
}
// Called by Lua to delete an object
static int object_delete( lua_State *L ) {
Object *obj = lua_touserdata( L, 1 );
// Remove the objects self reference
luaL_unref( L, LUA_REGISTRYINDEX, obj->self );
return 0;
}
// Called from Lua to set a callback function
static int object_set_callback( lua_State *L ) {
Object *obj = lua_touserdata( L, 1 );
// Unref an existing callbacks
if ( obj->callback != 0 ) {
luaL_unref( L, LUA_REGISTRYINDEX, obj->callback );
obj->callback = 0;
}
// Set the new callback - function is on top of the stack
obj->callback = luaL_ref( L, LUA_REGISTRYINDEX );
}
// Called from C to call a Lua function for the obj
static void do_callback( Object *obj ) {
// Push the Lua function onto the stack
lua_rawgeti( L, LUA_REGISTRYINDEX, obj->callback );
// Push the userdata onto the stack
lua_rawgeti( L, LUA_REGISTRYINDEX, obj->self );
// Call the function
lua_call( L, 1, 0 );
}
Is there some way I can set the object to nil in Lua, and have the delete() method called automatically? Alternatively, can the delete method nil all variables that reference the object? Can the self reference be made 'weak'?
EDIT 1: I've included code to show why the object references itself; see the do_callback function. Each object is part of a tree-like structure, with the bulk of the processing done in C, but a user can set a custom Lua function that is called under certain conditions.
EDIT 2: Another possible solution comes to mind; Instead of each obj keeping a reference to itself, can I lookup the object in the global index when I need to pass it to Lua, using its address as a key?
You could try creating a weak table in the registry and store your references there, that way setting all references of your object to nil should make it available for the gc.