ducc's Tip of the Day

Started by ducc · one year ago · 1 reply · 379 views

#1
ducc
Banned
JoinedSep 2022 Posts101 Score155
Tip #1: Use ChangeAttribute, and use it with nameof.

I'll use the term "change method" to refer to the method named in ChangeAttribute.

For example: in the below snippet, the change method would be RemakeCommandList.
[Property, Change( nameof(RemakeCommandList) )]
public CameraComponent MainCamera { get; set; }

private void RemakeCommandList( CameraComponent oldCamera, CameraComponent newCamera )
{
    // Ensure that our command list is no longer used by the previous camera.
    if ( oldCamera.IsValid() && _commandList is not null )
    {
       oldCamera.RemoveCommandList( _commandList );
    }
    
    // Make a new command list, because we want to tie its lifetime to our choice of camera.
    _commandList = new CommandList( nameof(SceneViewManager) );
    
    if ( newCamera.IsValid() )
    {
       newCamera.AddCommandList( _commandList, RenderStage, RenderOrder );
    }
}
Now, here's why I think you should use ChangeAttribute.

First, checking in the setter of a property whether value matches the the backing field is tedious boilerplate. This may be replaced with the less tedious boilerplate of making a change method.

Compare:
[Property]
public Action<CameraComponent> OnMainCameraChanged { get; set; }

[Property]
public CameraComponent MainCamera 
{ 
    get => _mainCamera;
    set
    {
        var hasValueChanged = value != _mainCamera;
        _mainCamera = value;
        if ( hasValueChanged )
        {
            OnMainCameraChanged?.Invoke( _mainCamera );
        }
    }
}
private CameraComponent _mainCamera;
to:
[Property]
public Action<CameraComponent> OnMainCameraChanged { get; set; }

[Property, Change( nameof(RemakeCommandList) )]
public CameraComponent MainCamera { get; set; }

private void RemakeCommandList( CameraComponent oldCamera, CameraComponent newCamera )
{
    OnMainCameraChanged?.Invoke( newCamera );
}

Some points about this:
  • The code within a change method is free to assume that a change has occurred.
    • This removes the need to use a local to keep track of whether the value has changed.
    • If you have an Action or event that should be called whenever a property changes, you may simply call it at the end of a change method. 
  • Typically, adding a change method to a property removes the need for that property to have a backing field.
  • The change method may be called whenever its behavior is desired.
    • This is made easier by the fact that you can name the method and its two parameters whatever you'd like.
Now, why should use use nameof?

You could write your change attribute like this:
[Change( "RemakeCommandList" )]
But doing so is prone to human error. If you make a typo in the string here, you will not cause a compiler error, so the problem might not be noticed until you run the game. However, if you make a typo in nameof, you'll almost certainly see a compiler error, barring the unlikely case that you accidentally typed the name of something that actually exists.

Finally, using nameof means that if you were to rename the change method without renaming the name given to ChangeAttribute, the compilation would fail with an error. This is a better alternative to the compiler successfully compiling a program with incorrect behavior, which is what could happen if you were using a string literal instead of nameof in ChangeAttribute.
#2
Dimmer
Member
JoinedMar 2023 Posts61 Score1,250
Do you have any tips for my marriage?
people
Log in to reply
You can't reply if you're not logged in. That would be crazy.