DIP5
Title: | Properties 2 |
---|---|
DIP: | 5 |
Version: | 1 |
Status: | Superceded |
Created: | 2009-07-27 |
Last Modified: | 2009-07-28 |
Links: | Original Prowiki Page |
Abstract
An alternative design of properties. A variant of DIP4.
Rationale
Parts of DIP4 are too puristic. Especially this one:
class Foo
{
int width = int.init
{
get { return value; }
set { value = set; }
}
}
Description
Namespaces were proposed as a solution for the properties problem:
class Foo
{
namespace width
{
int val;
int opGet() { return val; }
int opSet(int newVal) { val = newVal; }
//other functions, classes, variables
}
}
They can be a cool feature but they don't look like properties.
Properties can be implemented similarly to namespaces:
class Form
{
// Full syntax
int width
{
private int val;
int opGet() { return val; }
int opSet(int newVal) { return val = newVal; }
}
// Default implementation declaration.
int height { default; }
EventHandler onClick
{
private EventHandler[] invocationList;
auto opCatAssign(EventHandler h) { return invocationList~=h; }
void opCall() { invocationList[](EventData.Default); }
}
}
Default storage is not a big deal to worry so much about it. The {get;set;} syntax exists in C# only for reflection purposes: librarier such as NHibernate are implemented so they look only for properties while serializing objects, so some default implementation for property-as-a-field is needed there. Another usecase for it is enforcing properties with interfaces, while most properties are trivial. And it seems to be the only usecases for default implementation for property getter, setter and backing storage. Though the same syntax is used for property declaration in interfaces. If one writes nontrivial getter or setter, explicit declarations of backing storage and setter parameter are no more an issue.
The problem with hidden backing storage is C# programmers for some reason used to access an event's invocation list and rearrange it, though getting access to it is tricky.
As to typename duplication, type inference can be used for opGet and opSet.
class Form
{
int width
{
//auto declaration without initializer can be resolved
//to the enclosing property type
private auto val; //can be an error if you wrote auto by mistake
//such type inference is planned for lambda expressions either
opGet() { return val; }
opSet(newVal) { return val = newVal; }
}
}
Interface declaration.
interface IControl
{
int width
{
opGet();
opSet(int);
}
int height { default; } //the same as above
string title { opGet(); }
}
Problems
- Namespaces have the same ambiguity as current properties have, but in a more severe form:
int delegate() foo() {
return delegate int(){ return 5; };
}
// Is this an int or a delegate?
auto x = foo();
//is opGet a member of property or of its return value?
auto x = form.width.opGet();
So virtually no property member can be accessed reliably (guanteeing no clash with the return value members). This can be solved by making properties fully transparent as they are in C#.
- Possible misuse
int width
{
int val;
float opGet() { return val; }
}
- Peculiar ambiguity: with nested methods whether the member is a property or a function becomes a matter of minor detail: braces after the member identifier.
int width
{
int val;
int opGet() { return val; }
}
int incrementWidth()
{
int val;
float get() { return val; }
return get();
}
This may be not a big issue, but still...
Copyright
This document has been placed in the Public Domain.