问题
I define a simple struct this way:
struct Person{
private string _name;
@property ref string name() { return _name; }
}
The @property
annotation is really cool, but I don't know how should I use it properly.
The above is quite ok, but I cannot pass a Person
to a function requiring in Person
for instance:
void fun(in Person p) { ... }
To avoid copying Person
I have to declare the parameter with ref
, though I don't modify it.
So how to combine the property syntax with const-correctness?
edit: To follow up, can the same apply to looping?
void fun(in Person[] people) {
foreach(Person p; people) { ... }
}
now I don't want to copy person, but I can't use ref Person
since it's const. So I have to write ref const(Person) p
in the loop which becomes loong.
回答1:
Normally, what you'd do would be
@property string name() const { return _name; }
@property void name(string value) { _name = value; }
and you wouldn't bother with ref
(certainly, for a string
, there isn't much point). For more complex types that you want to avoid copying, you can return by const ref
, e.g.
@property ref const(Foo) foo() const { return _foo; }
@property void foo(Foo value) { _foo = value; }
You could overload the setter so that it accepted ref Foo
in addition to Foo
, but there isn't much point, since you'd be copying the passed in Foo
to assign it to _foo
anyway.
And if you really want to, you can return by ref
from the getter and overload it, e.g.
@property ref const(Foo) foo() const { return _foo; }
@property ref Foo foo() { _foo; }
in which case the non-const overload can be used as a setter, but if you're going to do that, why bother with a property? At that point, you might as well just make the member variable public, because the property isn't protecting it at all. By returning non-const ref
, you've lost control over how the member variable is set and have effectively exposed it as a public member variable except that you have the extra plumbing of the function around it. The only advantages that it gives you are that you can do something before returning and that the type's invariant (if any) will be called when the property is called (whereas it won't with a public member variable), but because the variable can be set without you're control at that point, those benefits are of questionable value in comparison to the simplicity of simply making the member variable public.
So, in general, the first example is the way to go, and once in a while, the second is better, but it's arguably pointless to go with the third example.
EDIT:
As Kozzi11 points out, you can implement the 3rd example as
@property auto ref foo() inout { return _foo; }
or
@property ref inout(Foo) foo() inout { return _foo; }
instead of having two functions, but my point about it not really being much better than a public member variable still applies.
EDIT 2: With regards to your edit to the question...
If you want to avoid copying in the loop, you're going to have to be explicit with the type.
foreach(p; people) { ... }
will work, but it will copy each individual Person
as it iterates over people
, whereas
foreach(ref Person p; people) { ...}
or
foreach(ref const(Person) p; people) { ...}
will avoid copying each Person
.
回答2:
What about this:
import std.stdio;
void someFun(in Person person) {
writeln(person.name);
}
struct Person {
private string _name;
@property auto ref name() inout { return _name; }
}
void main(string[] args)
{
auto person = Person("Osoba Nova");
someFun(person);
stdin.readln;
}
EDIT: for loop you can ommit type
void fun(in Person[] people) {
foreach (p; people) {
writeln(p.name);
}
}
回答3:
a property function is just a function, so you can overload it.
@property ref const(string) name() const { return name_; }
@property ref string name() { return name_; }
来源:https://stackoverflow.com/questions/24160745/provide-property-for-const-and-non-const-structures-in-d