Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove underscores in front of C# "lifecycle" methods to match the language's conventions #14466

Closed
levrik opened this issue Dec 9, 2017 · 87 comments

Comments

@levrik
Copy link

levrik commented Dec 9, 2017

Not sure if this was brought up before already but I'm confused by the naming of the "lifecycle" methods (e.g. _Ready, _Process) in C#. They also have the underscore in front like GDScript.
So far as I understand why this was needed in GCScript I'm not sure if it's needed in C#. It feels kinda weird to me since we already have the override keyword in front.

@akien-mga
Copy link
Member

CC @neikeq @Hinsbart

@neikeq
Copy link
Contributor

neikeq commented Dec 9, 2017

I guess we could remove them. No idea if it may cause conflicts though, it's a matter of trying. It's also a simple task, changing it here should be enough:

imethod.proxy_name = escape_csharp_keyword(snake_to_pascal_case(imethod.name));

@NathanWarden
Copy link
Contributor

Not that I mind the underscore, but it probably would be less weird to not have it :) If nobody has already taken this task I'll go ahead and tackle it if nobody else wants to do it.

@NathanWarden
Copy link
Contributor

@neikeq There are a few naming conflicts for some of the method names. For instance, there's a "Get" method on Object, but then there's a virtual "_Get" method. So we end up getting a conflict when the underscore gets removed.

There are a number of solutions that could be used, but the first one that comes to mind is to replace the underscore with "On". So, "OnReady", "OnProcess", etc... I think this would be more consistent/expected compared to other API's.

I tried this and everything is working conflict free.

Ideas? Thoughts?

@neikeq
Copy link
Contributor

neikeq commented Dec 9, 2017

We would need to hear the opinion of users about that. Personally, I don't have anything against it. Is there any name that doesn't sound right with the "On" prefix?

@RayKoopa
Copy link
Contributor

RayKoopa commented Dec 9, 2017

OnXxx seems fine to me. Even though, at some .NET 1.0 time, it was typically used / meant to be used only for tiny helper methods which raise an event with given parameters (Windows Forms most prominently), many people overrode that in their sub classes to indeed simply execute their own code when the event was about to be raised (sometimes not even calling the base implementation so the event was never raised again in sub classes).

Slightly dumb example given you couldn't simply override Text here:

public class Button
{
    private string _text;

    public string Text
    {
        get => _text;
        set
        {
            string oldText = _text;
            _text = value;
            OnTextChanged(oldText, _text);
        }
    }

    public event TextChangedEventHandler TextChanged;
    
    protected virtual void OnTextChanged(string oldText, string newText)
    {
        TextChanged?.Invoke(this, new TextChangedEventArgs(oldText, newText);
    }
}
// And people typically did
public class FancyAssButton : Button
{
    private string _textToDraw;
    protected override void OnTextChanged(string oldText, string newText)
    {
        // Oops, didn't call base, but who cares, right? Right.
        _textToDraw = ConvertTo1337Speak(newText);
    }
}

So it has been abused over time to become exactly what we want it to be, a place to put custom code for handling events.

It's definitely better than the underscore which outright violates C# naming rules. Let's not be as bad as Unity in that matter (with it's ugly lowercase public instance members) :)

Plus I can't think of a better prefix otherwise, except HandleXxx, but it's lengthy and sounds dumb.

@NathanWarden
Copy link
Contributor

They seem to work, but here's a full list if anyone wants to chime in:

AStar.OnComputeCost
AStar.OnEstimateCost
BaseButton.OnPressed
BaseButton.OnToggled
CanvasItem.OnDraw
CollisionObject.OnInputEvent
CollisionObject2D.OnInputEvent
Control.OnGetMinimumSize
Control.OnGuiInput
EditorExportPlugin.OnExportBegin
EditorExportPlugin.OnExportFile
EditorResourceConversionPlugin.OnConvert
EditorResourceConversionPlugin.OnConvertsTo
EditorSceneImporter.OnGetExtensions
EditorSceneImporter.OnGetImportFlags
EditorSceneImporter.OnImportAnimation
EditorSceneImporter.OnImportScene
EditorScript.OnRun
MainLoop.OnDropFiles
MainLoop.OnFinalize
MainLoop.OnIdle
MainLoop.OnInitialize
MainLoop.OnInputEvent
MainLoop.OnInputText
MainLoop.OnIteration
Node.OnEnterTree
Node.OnExitTree
Node.OnInput
Node.OnPhysicsProcess
Node.OnProcess
Node.OnReady
Node.OnUnhandledInput
Node.OnUnhandledKeyInput
Object.OnGet
Object.OnGetPropertyList
Object.OnInit
Object.OnNotification
Object.OnSet
RigidBody.OnIntegrateForces
RigidBody2D.OnIntegrateForces
TileSet.OnForwardSubtileSelection
TileSet.OnIsTileBound
VisualScriptCustomNode.OnGetCaption
VisualScriptCustomNode.OnGetCategory
VisualScriptCustomNode.OnGetInputValuePortCount
VisualScriptCustomNode.OnGetInputValuePortName
VisualScriptCustomNode.OnGetInputValuePortType
VisualScriptCustomNode.OnGetOutputSequencePortCount
VisualScriptCustomNode.OnGetOutputSequencePortText
VisualScriptCustomNode.OnGetOutputValuePortCount
VisualScriptCustomNode.OnGetOutputValuePortName
VisualScriptCustomNode.OnGetOutputValuePortType
VisualScriptCustomNode.OnGetText
VisualScriptCustomNode.OnGetWorkingMemorySize
VisualScriptCustomNode.OnHasInputSequencePort
VisualScriptCustomNode.OnStep
VisualScriptSubCall.OnSubcall

@levrik
Copy link
Author

levrik commented Dec 10, 2017

@NathanWarden @RayKoopa The only methods where the On prefix sounds a bit odd are Process and PhysicsProcess. They are no event-like methods.

Edit: I think same applies to MainLoop.OnIdle but I'm not sure what this method actually does.

@RayKoopa
Copy link
Contributor

Sounds totally fine to me. OnIsTileBound is what sounds horrible to me.

@levrik
Copy link
Author

levrik commented Dec 10, 2017

@RayKoopa Uh. Didn't saw that one.

@NathanWarden
Copy link
Contributor

@RayKoopa Yeah, haha, that one is a bit odd :) I'm almost certain we could make a blacklist of exceptions for this.

@levrik I agree those sounded slightly odd to me too until I thought about what was really going on. IE. they are really about the event of the frame or physics being finished and here's how long they took (delta). I'm fine with the names, but for sake of clarity of what I'm saying, is that they could have been more accurately called OnFrameProcessed or OnPhysicsProcessed.

@levrik @RayKoopa @neikeq Since we'll most likely need a blacklist of some methods, what do you guys think about Process/PhysicsProcess having "On". I'm casting my vote for having "On" for them since they are technically events, but am also not dogmatic about that either.

Finally, @neikeq if a blacklist sounds good, do you have a preference where this should be and data type? The most obvious would just be sticking it right in the function as an array/hashtable.

@akien-mga
Copy link
Member

akien-mga commented Dec 10, 2017

I'm fine with the names, but for sake of clarity of what I'm saying, is that they could have been more accurately called OnFrameProcessed or OnPhysicsProcessed.

No blacklist please. You can do what you want to make the C# bindings more idiomatic, but it should all be automated conversion that anyone can perform in their heads to go from GDScript to C# and back when familiar with both languages. Renaming _process to OnFrameProcessed means effectively to change the Godot API and break compatibility with other languages. If you think that the Godot callback should be _frame_processed, that's another discussion separate from Mono bindings.

The aim is not to be like "ah, this Godot API is awkward, let's hack the Mono bindings so that it's less awkward". If an API is awkward, it should be fixed for everyone or noone.

@levrik
Copy link
Author

levrik commented Dec 10, 2017

@akien-mga I don't think @NathanWarden proposed to change that in the C# bindings only.

@RayKoopa
Copy link
Contributor

I agree on not having a blacklist, that makes everything needlessly complicated. If the prefix is bad, we need another one. If you feel crazy, even something like GDReady, GDIsCrankyPants :/.
Best thing would still be to have no prefix at all. I haven't seen all the conflicting names yet. With the example given above of Get and _Get, having two methods Get and OnGet doesn't sound much wiser too. Maybe (if possible?) one can be renamed completely to be more specific anyway, like GetProperty, but then again I haven't even used the C# bindings yet so I'm not sure if that's wise aswell.

@NathanWarden
Copy link
Contributor

NathanWarden commented Dec 10, 2017

@akien-mga What @levrik said is what I meant. It wasn't a name change proposal. I was only using the name OnFrameProcessed as an example to show that the Process function is actually an event and that it could have just as easily been named OnFrameProcessed, therefore calling it OnProcess still makes sense because it is actually an event of the frame having been processed despite OnProcess not sounding like an event.

As far as IsTileBound... is this even a proper name for this function? It looks like it should actually be called TileIsBound or simply TileBound because this is the method signature. Notice there are no booleans despite it's name implying them. @akien-mga Now this time, haha, I'm possibly proposing a name change :D

void IsTileBound(int drawnId, int neighborId)

I think it's misnamed? Can somebody who's familiar with TileSet chime in? There's no documentation for it and I don't know what this method does :)

@akien-mga
Copy link
Member

void IsTileBound(int drawnId, int neighborId)

That would be a bug in the method binding, it's a virtual method and it is supposed to return a boolean (well actually a Variant):

@akien-mga
Copy link
Member

The wrong is_tile_bound return value should be fixed in #14505, currently building to generate the mono glue and confirm.

@NathanWarden
Copy link
Contributor

Ah, okay, thanks for clearing that up :)

So, just to be clear, the blacklist I was talking about would be to not put "On" in front of certain methods like IsTileBound. The rest would be OnXxx, IE. OnProcess, OnPhysicsProcess, OnReady, but then you'd have IsTileBound instead of OnIsTileBound.

Are you fine with that, or do you think that will be problematic? Out of about 40 or 50 methods we literally have 1 that On doesn't make sense.

I'm not sure there's really any other prefix to put that would make sense on all methods. Putting GD would work, but I think that's more strange than just having an underscore. Like I said in my initial post, I'm not against keeping the underscore, but just that I agree that it's a little strange to have.

@RayKoopa
Copy link
Contributor

IMHO no blacklist, not even for a single method. A developer would wonder why all other methods handling Godot stuff are prefixed with On, but that one is not. Even if the name is weird like OnIsCreepyMethod, that name is still easier to understand than this arbitrary "hacky" exception.

@akien-mga
Copy link
Member

Are you fine with that, or do you think that will be problematic? Out of about 40 or 50 methods we literally have 1 that On doesn't make sense.

No, there are a lot more than that. Any virtual method bound to scripts that returns non-void is basically not an event. See e.g.:

AStar.OnComputeCost
AStar.OnEstimateCost

Those are not events, they're methods you can override to return a float which is used in AStar's algorithm. You can define your own cost estimation and computation logic using those methods (in GDScript, _compute_cost and _estimate_cost.

So that's to see with @neikeq who knows this stuff better, but it might be worth separating the bulk of BIND_METHODV stuff between the real events/callbacks, and the Node-specific methods you can override to customize behaviour like _is_tile_bound, _compute_cost or drag_data.

As @RayKoopa says, there should not be any blacklist/no exceptions. If the virtual methods have different natures, they should be better separated at the engine level, not in the bindings for each language.

@NathanWarden
Copy link
Contributor

NathanWarden commented Dec 10, 2017

Yep, I agree. Then, I think it would be best to just leave it as underscore? :/ (edit: It does keep it consistent with GDScript, and therefore the documentation as at least one positive thing)

HandleXxx would probably be fine, but as @RayKoopa mentioned it makes it more lengthy and doesn't sound very good.

But, I'll be out for about half the day. So if you guys can think of something better, when I get back I can just drop it in and I can generate a new list that I can post so we can see if it works :)

@vnen
Copy link
Member

vnen commented Dec 10, 2017

I don't think _is_tile_bound should be prefixed anyway. Nor the ones in VisualScriptCustomNode. For instance, the virtual methods on EditorPlugin are not prefixed.

I think this is a good opportunity to review them and break compatibility if need be.

@akien-mga
Copy link
Member

I think this is a good opportunity to review them and break compatibility if need be.

I agree, but it should be discussed in its own issue :)

@Zylann
Copy link
Contributor

Zylann commented Dec 10, 2017

OnGet/OnSet are weird to me. Or in general, I always saw On* methods as event/signal handlers, and as such, methods that don't return a value and that are called by a notification/event system...

@Zylann
Copy link
Contributor

Zylann commented Dec 10, 2017

Also, underscore in front of methods often means that the method is not public, which is specified already in C# (and not in GDScript), so maybe removing the undersore and making the method protected would be enough.

@neikeq
Copy link
Contributor

neikeq commented Dec 10, 2017

I think we should just keep the underscore, it doesn't look bad to me and makes it more familiar for existing Godot users. That being said, we should ask users about this to have a better idea of what they think (probably in the facebook group where most people is).
I agree with @akien-mga. I'm against a black-list. The less bindings we have to maintain the better, so it should be automatic. I prefer OnIsTileBound (which sounds bad), to a black list.
@Zylann That's the first thing that @NathanWarden tried, but there are conflicts with other methods when removing the underscore.

@NathanWarden
Copy link
Contributor

NathanWarden commented Dec 11, 2017

@Zylann I definitely would have liked just the method names too, but that won't work without changing several Godot method names, which would also affect GDScript, which means we'll need to get more people involved than just those of us interested in C#.

I agree with @neikeq. I think we should just keep the underscore. It isn't ideal, but it really works just fine. I personally never even noticed a problem with it until it was brought up, haha. But, maybe that was just from using GDScript with Godot up to that point. :)

Plus, we could always revisit this in the future. IE. If enough people want it changed we could change it in something like Godot 4 (or a Godot 3 point release if it makes sense). I think it'll be far more obvious how we should proceed after Godot 3 is released whether this is an issue for the broader community (Facebook/Twitter/etc) or not.

@NathanWarden
Copy link
Contributor

I'm all for removing the underscores, as many people on this thread already know I tried, but the cure for this seems to be more troublesome than the disease.

@aaronfranke
Copy link
Member

aaronfranke commented May 30, 2018

Based on everything I know about this issue, that removing would be a pain and possibly have inconsistencies, for a minor style benefit, I vote to keep the underscores.

By the way, I personally prefer the underscores to On prefixes.

@jonbonazza
Copy link
Contributor

jonbonazza commented May 30, 2018

What frustrates me is that people call it a "mild style benefit." The fact that very few people seem to understand the importance of conforming to standards and conventions (and standard conventions) is this most disappointing part of this whole thing.

@mysticfall
Copy link
Contributor

If there's significant practical hurdles to fix this issue, I agree that we should leave it as it is. I'd like to see it fixed though but based on what I read in this thread, it seems it won't happen unless someone will take the job him/herself and makes a PR.

I believe the majority of us agree that it's better to fix it so our codebase could be more conforming to the standard coding convention by now. As such, if we need to discuss this matter further I guess it better be on the technical side, so we can hopefully find a way to overcome whatever obstacles for fixing this before 3.1, if it's feasible at all.

@aaronfranke
Copy link
Member

@jonbonazza The standard which we are conforming to, in this case, is the Godot API.

@mysticfall
Copy link
Contributor

mysticfall commented May 30, 2018

@aaronfranke But in this case, it's a standard that is mostly pointless in the context of C# which also conflicts with the widely used conding conventions of the language, so I agree with what @jonbonazza said in principle.

And if we are to regard whatever coding convention that Godot API suggests, probably we shouldn't have tried to convert all snake_case names to PascalCase ones in the first place.

But as I said, I can see how fixing the issue involves some real work while there are other issues which better be fixed before 3.1. So I just hope someone who knows internals of how glue generation works to take some time to contribute.

@NathanWarden
Copy link
Contributor

If anyone wants to give it a try they can reference my code here, although it's a bit outdated: https://github.com/NathanWarden/godot/tree/cs_virtual_method_rename

@neikeq
Copy link
Contributor

neikeq commented May 30, 2018

Guys, the problem is not implementing it. I would gladly implement it myself if there was anything to implement. I explained what the problem was. If you want to see this done, you should try proposing a solution for the conflicts.

@mysticfall
Copy link
Contributor

mysticfall commented May 30, 2018

@neikeq I think I understand the problem, since what I meant by 'implementing' certainly includes finding a suitable way to resolve the Get vs _Get situation that I mentioned above.

Maybe we could get a more clear idea of the problem if you could explain the difficulties involved in that specific case.

So, I'd like to ask what would happen if we simply remove _Get and make Get as a virtual method instead? Would it break any existing functionalities? And, can we assume every other conflicts would be similar to this particular case, so the solution we may find for Get / _Get can be applied universally?

@neikeq
Copy link
Contributor

neikeq commented May 30, 2018

What frustrates me is that people call it a "mild style benefit." The fact that very few people seem to understand the importance of conforming to standards and conventions (and standard conventions) is this most disappointing part of this whole thing.

Like I said previously, I really don't understand the need to not deviate one bit from a standard convention but, as explained above, that's not the reason this is not changed. Many changes were already made to please C# users, even though I may not agree with many of them.

@neikeq
Copy link
Contributor

neikeq commented May 30, 2018

@NathanWarden shared an screenshot of the errors he gets when removing underscores in thi comment. Reading that comment, I see why there was the confusion above about these methods being internal.

The list of conflicts seems to be:

  • Control
    • _GetMinimumSize with GetMinimumSize
  • Object
    • _Get with Get
    • _GetPropertyList with GetPropertyList
    • _Set with Set
  • BaseButton
    • _Pressed with Pressed
  • Resource
    • _SetupLocalToScene with SetupLocalToScene
  • MainLoop
    • _Idle with Idle
    • _InputEvent with InputEvent
    • _InputText with InputText
    • _Iteration with Iteration

At first glance, one would say we should have a single method. There is a problem in C# with sharing the same method for both get and _get though, see:

class Object:
    virtual func get(property): # wrapper function which calls the Engine's Object.get
        internal_engine_call_Object__get(property)

class MyScript extends Object:
    override func get(property):
        # custom code here
        return base.get(property)

Engine's Object.get is called, it doesn't find the property so it falls back to calling MyScript._get, which in this case would be MyScript.get. MyScript.get does its thing and calls the Object.get wrapper method, which then again calls Engine's Object.get which falls back to MyScript._get and so on... It's an infinite loop.

@mysticfall
Copy link
Contributor

Thanks for the explanation. Then, it seems that the heart of the matter is that the native method is invoking its virtual counter part (as I also suspected in my comment above).

In that case, the only remedy I can think of is to use On- or Do- prefix for the virtual method.

However, even though using such a prefix is quite common, it's not a part of any conding standard that I know of. On the other hand, names of non-private methods are covered by the official coding guideline from MS, which clearly states they should follow PascalCase naming.

So, using something other than _ might have some benefit of making it more conforming to the standard coding convention.

But simply using either of those proposed alternatives for all cases probably would result in unnatural names. For example, while DoGet might be quite a common choice in such a situation, names like DoPressed won't make much sense.

Maybe it'd make more sense if we can extend the ClassDB format so that we can specify different names for a specific binding by hand (and possibly with other informations, like what additional interfaces it'd extend, and etc - i.e. #17791).

But if must rely on blind replacing of auto generated method names, I'm not sure if it'd be worth the hassle at this point. So I have to agree it'd be better to leave it as it is, even though I'd like to see Godot's API to be as much standard conforming as possible.

@nathanfranke
Copy link
Contributor

I agree with @aaronfranke; C# methods should be consistent with GDScript methods. If it is stupid to have an underscore before each lifecycle method, what about renaming the GDScript methods also?

Unpopular opinion: How about we implement function overrides in GDScript and just use func ready() and void Ready()?

@mysticfall
Copy link
Contributor

In my opinion, it would be better C# methods to be consistent with the official convention of the language rather with another language which follows different rules.

I agree that we shouldn't make it confusing for people who may switch between the two languages to use Godot. However, I don't think it'd be too difficult for them to see which GDScript method to correspond with a C# one, as long as there's a consistent rule (e.g. snake_case to PascalCase).

As to lifecycle methods, it seems that the only reason why GDScript adopted such a convention is that it does not have override keyword as C# does. So I don't see any reason why C# should follow such a foreign convention when it already diverges from GDScript in many other aspects when they seem to fit its standard better.

@Shadowblitz16
Copy link

Shadowblitz16 commented Apr 28, 2020

@mysticfall
gdscript already isn't consistent.

for example signals are named ready while their implementations are named _ready.

I think we should follow the C# style guidelines and use On prefex for hooks and signal implementation.

an example would be void OnReady() {}.

Do doesn't really make sense and doesn't follow the conventional C# style guidelines.

their editor counter parts would remain without _ or On.

@neikeq
Copy link
Contributor

neikeq commented Apr 28, 2020

Is the On prefix used in the C# style guidelines for hooks though? As far as I understand, it's actually used for the method that raises an event, e.g.:

event Action<string> TextChanged;

void OnTextChanged(string text) {
    TextChanged?.Invoke(text);
}

@Shadowblitz16
Copy link

its used for the functions that implement the logic

@Flavelius
Copy link
Contributor

In my opinion, On[X] makes sense because _ready etc are like engine events in a broader sense (which makes it a nice hint towards their intention too). And Regular methods are more likely to be imperatively named which means 'Ready()' Set() Go(), are free to be used by programmers. Plus regular event subscriptions are a little less likely to be overrides at the same time.

@neikeq
Copy link
Contributor

neikeq commented May 1, 2020

its used for the functions that implement the logic

Any reference? From what I've seen, this is not the case, and the convention is to use it on methods that raise the event as in my example.

In my opinion, On[X] makes sense because _ready etc are like engine events in a broader sense

The motivation for getting rid of the underscore prefix is that it looks out of place when considering standard conventions, so it doesn't make sense to change it to something that goes against standard conventions as well.

@neikeq
Copy link
Contributor

neikeq commented May 1, 2020

I'll give a try at removing the underscore soon and see what the resulting conflicts are. If the conflicts are very few and for unimportant methods then I think we can move forward with the change, only keeping the underscore when there are conflicts.

@JimArtificer
Copy link

Today the naming is internally consistent. Don't sacrifice that for a little syntactic sugar.

@Calinou Calinou added this to the 4.0 milestone Nov 26, 2020
@Calinou Calinou changed the title [C#] Underscores in front of "lifecycle" methods Remove underscores in front of C# "lifecycle" methods to match the language's conventions Nov 26, 2020
@Calinou
Copy link
Member

Calinou commented Jan 31, 2021

Closing in favor of godotengine/godot-proposals#757, as feature proposals are now tracked in the Godot proposals repository.

@Calinou Calinou closed this as completed Jan 31, 2021
@Calinou Calinou removed this from the 4.0 milestone Jan 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests