Feature request: add storage to class with extensions

I know that this is normally not possible. thats why I use a work around:

The code:

type ext = public extension class(Object)
    public var fNewStorage: String;
end;

will be expanded by the compiler to:

type _ext_Storage = public static class
    public _Storage_fNewStorage := new System.Collections.Generic.Dictionary<Object, String>;
end;

type ext = public extension class(Object)
    public property fNewStorage: String read getfNewStorage write setfNewStorage; 
    private method getfNewStorage: String;
    begin
        var st := _ext_Storage._Storage_fNewStorage;
        if not st.ContainsKey(self) then 
            exit &default(String)
        else
            exit st[self];
    end;
    private method setfNewStorage(value: String);
    begin
        var st := _ext_Storage._Storage_fNewStorage;
        if value = &default(String) then
        begin
            if st.ContainsKey(self) then st.Remove(self);
        end
        else if not st.ContainsKey(self) then 
            st.add(self, value)
        else
            st[self] := value;
    end;
end;

Pro
Gives the possibility to add storage to an existing class

Con
The generated dictionary will prevent the instances that used the new storage from being garbage collected, /edit/ unless it has been set to the default value of the type first, because that removes it from the dictionary.

Edited the generated code to be more efficient.

Another edit, to eliminate the con - by using a weak reference, the instances will be garbage collected:

type _ext_Storage = public static class
    public _Storage_fNewStorage := new System.Collections.Generic.Dictionary<WeakReference, String>;
end;

type ext = public extension class(String)
    public property fNewStorage: String read getfNewStorage write setfNewStorage; 
    private method getfNewStorage: String;
    begin
        var key := GetKeyOnTarget(self);
        if key = nil then
            exit &default(String)
        else
            exit _ext_Storage._Storage_fNewStorage[key];
    end;
    private method setfNewStorage(value: String);
    begin
        var key := GetKeyOnTarget(self);
        if key = nil then
        begin
            //not in dictionary
            if value <> &default(String) then 
                _ext_Storage._Storage_fNewStorage.Add(new WeakReference(self), value);
        end
        else
        begin
            //in dictionary
            if value = &default(String) then 
                _ext_Storage._Storage_fNewStorage.Remove(key)
            else
                key.Target := value;
        end;
    end;
    private method GetKeyOnTarget(target: String): WeakReference;
    begin
        for each k: WeakReference in _ext_Storage._Storage_fNewStorage.Keys do
        begin
            if k.Target = target then
                if k.IsAlive then
                    exit k
                else
                begin
                    _ext_Storage._Storage_fNewStorage.Remove(k); //clean up 
                    exit nil;
                end;
        end;
        exit nil;
    end;
end;
1 Like

I don’t know whether your request makes sense to RemObject team, but as an Oxygene newbie, I surely picked up and learned new knowledge from your writing! :thinking::face_with_monocle:

How can I find and learn this type of “advanced” and “under-the-hood” knowledge? I don’t think I saw it mentioned in the documents or any samples. Or should I just go ahead and read the RTL source code? Advice?

Hey Theo,
I get all that… Why not just create a new MyObject based on Object rather than using an extension?
Or am I missing something ?
Paul

Paul,

This is meant a a generic solution.
With extension you can at this moment extent any class with functional properties and methods.
But you can not extend a class with fields or storage properties; you can not add storage to an existing class.
This solution adds extension fields / extension storage to an existing class.

If you want b.e. to add a Tag field to an existing class, this is currently not possible.
This solution makes that possible.

Wuping,

I use codeproject a lot for reference at advanced topics - most is in C# but that is not that different from Oxygene. The weakreference I found in the Microsoft docs.
So just a point of doing good google searches :smile:

Edit: pressed enter too quick …

@mk, @ck,
I could implement this with aspects like:

type ext = public extension class(Object)
    [ExtendStorage]
    public var fNewStorage: String;
end;

But the editor syntax check won’t allow for:

type ext = public extension class(Object)
     [ExtendStorage]
    public var fNewStorage: String;
    public property MyProp read fNewStorage write fNewStorage;
end;

I think I’d prefer an Aspect to the compiler doing this for what, naïvely, looks like a simple field declaration. There’s compiler magic, and then there’s talking it too far… :wink:

Hi Marc

I like complete implementations.
If a class consists of methods, properties and fields, I like to be able to extend it with all three.
And I always ask first - best place for everything is in the language itself.
Second best are aspects - so I will try it with these.

Yes, but this solution just adds way too much black magic in the background, not to mention the side effects you mentioned wrt Garbage Collection, for it to be a clean “oh yeah sure, lets just add a field here” syntax… IMHO.

The side effects where solved - but no problem :wink:

1 Like

Thanks to Carlo I have it running now as an aspect.
I will publish it here when I have the time.

2 Likes