[AGS 4 compiler] Extenders have access to protected fields?

Started by Monsieur OUXX, Yesterday at 22:57:10

Previous topic - Next topic

Monsieur OUXX

Note: I wrote "AGS 4 compiler" in the subject as good practice to separate the threads but this probably applies to the current compiler too.

Header :
Code: ags
managed struct A {
   protected int _a;
};


import void FooBar(this A);

Body:
Code: ags
void FooBar(this A) {  
  this._a = 666;
}

Did I make a mistake? Do extenders really have access to "protected" fields?

I'm strictly asking -- not reporting a bug or anything. For example, I hope there's not plan to "fix" it because it facilitates a lot of things for me.
 

Crimson Wizard

Firstly, I think that this is not correct, and, for the reference, C# does not allow this, which is entirely logical, because that would break the encapsulation of the class. This defeats the purpose of protecting fields if anyone can write extension methods violating their protection.

But there are historical reasons for this behavior in AGS. The problem is that previously in AGS script some things could not be done without extension methods, or rather it would be less convenient to do: such as struct's attribute getter/setter.

Things could have changed with the new compiler in this regard though.

In summary, I think that ideally this should be disallowed, but
a) first we need to double check that there exist proper solutions to cover the use cases.
b) this will break alot of old script modules, so even if it's not allowed by default, there has to be a backwards compatible switch.

eri0o

Wait, I don't really agree with this. Firstly, this is properly documented in the manual

https://adventuregamestudio.github.io/ags-manual/ScriptKeywords.html#protected

Here is my perception of AGS Script, note I started really using AGS in 2017, after a friend telling me about it in 2016, so I am going to talk about people I didn't meet and have never had a conversation, so it's just my perception, from looking at code and feel of the ergonomics... I believe CJ learned C at some point early on and had fun which then lead to the first version of AGS... I think the pile of actions to execute at some point lead him to learn about interpreters, and also at this point he may have learned about C++ - but it's like C++98, so it's terrible C++ without all the useful things we are used in the world post C+11 (from 2011, so after he left AGS entirely). Anyway, going into interpreters I guess it had some things based on C itself, but it's like only the minimum stuff for the minimum to make a game at the time. The Engine API also mimicked C style too. Then at some point not to late he learns about C# (it's like C# with .NET 1), and then he starts working on the idea of the Editor made using C#. The things he learns, he filters to AGS Script, the concepts, but again only the minimum to make a viable game, thinking what is the minimum to implement. So AGS Script evolves to this thing that is less formal than C# and it has some particular ergonomics, but I am not sure how much is natural from trying to keep things simple (for practical development reasons) and how much was thought through. But anyway, we get this cool interpreted, type safe, ref-counted language, that has some C#-like flavor.

Anyway, C# current accessors docs reads like this

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers

Anyway so protected means it allows access from a derived class - in C#, that is the difference with private.

This SO comment is my exact feel of why it's not possible to do so with C#. Look, in C# I can still extend the class and get the access.

Now in AGS, I can extend the struct too, sometimes. I can't do so if it's like Character or some other type of built-in. But I can use an extension method.

Anyway, my feel to this is that protected bein accessible by extension methods, is fine if having this feature doesn't block anything is fine. If you want to have such restriction, it would be better to introduce a new private keyword.

Crimson Wizard

Quote from: eri0o on Today at 02:21:13Now in AGS, I can extend the struct too, sometimes. I can't do so if it's like Character or some other type of built-in. But I can use an extension method.

But the builtin classes that you cannot extend (currently) also do not declare their protected fields. They hide them differently: by not having in the API at all. You would not be able to access these regardless. The only structs that have protected fields in practice are user structs, and these may be extended.

The real question IMO here is: what's the use of accessing protected fields from extender methods, and may the same thing be achieved without breaking incapsulation principles?

Snarky

AGS 4 still does not have true polymorphism, right? Working around that is one of the main reasons I ever use this access to deliberately break encapsulation.

Crimson Wizard

Quote from: Snarky on Today at 05:20:39AGS 4 still does not have true polymorphism, right? Working around that is one of the main reasons I ever use this access to deliberately break encapsulation.

Could you please post an example?, because polymorphism may refer to a number of things.

Snarky

I meant virtual methods, where the implementation that gets called depends on the subclass of the instance.

I was thinking of cases where you need to access protected fields of the parent from a subclass method. (Polymorphism comes into it because lack of it means that the whole class structure and method logic needs to be different.) But on second thought this isn't breaking encapsulation at all, because a subclass is an instance of the parent class, and it is normal for the protected keyword to allow access in subclasses.

I wasn't thinking of extender methods directly for the parent type, because I nearly always use those to implement all methods, including those that are part of the struct declaration, simply because I find the syntax nicer than the ClassName::Method style. If this were restricted, I would have to rewrite all my function headers.

I do sometimes find it useful to add extender methods that are not part of the struct declaration, even outside of the primary script for that struct. For example, let's say I have a module and want a way to export the data to Json, but I don't want it to be part of the main module because it adds clutter for a use case many users will not need. I can put it in a separate script as an extender to the type. In this case, having access to protected fields is often convenient.

SMF spam blocked by CleanTalk