Possible to implement custom code style rules in Visual Studio?

I’ve been trying to research (with no clear resolution) how to implement custom warnings for my Unity projects in Visual Studio.

For example, I keep getting burned when I have a public field initialized in the field declaration (rather than Awake()) because there’s the very likely chance something in the inspector will silently override it. The Unity tutorials seem to be indiscriminate with how they initialize fields adding to the problem. So I was wondering if I could write a custom rule to trigger warnings for conditions I define. Example below.

public class MyClass: MonoBehaviour
{ 
    //prefer this
    public float speed;
    //over this
    public float speed = 5;
}

It seems in Visual Studio I can only enable/disable or set severity levels for existing rules. I can’t figure out how to set custom ones. I have a feeling this has to be possible but can’t quite find the right article or help topic to do it.

Related - if anyone has done this and has created a set of messages and warnings tuned to Unity, I’d love to see what you have.

Interesting. My IDE-fu isn’t strong enough to know if the rule language is good enough to figure this out.

BUT… for my dollar, I think there is great value in understanding that this:

… is not random at all.

I guarantee you will become a far more effective Unity user if you can instantly pinpoint that “something.”

And the compiler warning can only address one of the steps in the cascade.

This the cascade, btw:

Serialized properties in Unity are initialized as a cascade of possible values, each subsequent value (if present) overwriting the previous value:

  • what the class constructor makes (either default(T) or else field initializers)

  • what is saved with the prefab

  • what is saved with the prefab override(s)/variant(s)

  • what is saved in the scene and not applied to the prefab

  • what is changed in Awake(), Start(), or even later etc.

Make sure you only initialize things at ONE of the above levels, or if necessary, at levels that you specifically understand in your use case. Otherwise errors will seem very mysterious.

Here’s the official discussion: https://blog.unity.com/technology/serialization-in-unity

Field initializers versus using Reset() function and Unity serialization:

https://discussions.unity.com/t/829681/2

https://discussions.unity.com/t/846251/8

Oh yeah - it’s clearly not random. 100% agree. But in a lot of the code I’ve seen, I can’t discern any pattern as to why they could chose two different initialization approaches for completely analogous purposes. It just creates a lot of cognitive load in even a medium size project to have to memorize how everything is initialized.

So given that, I’m looking for ways to simplify. Even better if I can have some automation help me do it. I realize everyone / every team / every business unit will have their own preferences. Not here to argue for one set of preferences over the other. I just want to find a way to create automation to cater to my own personal preferences.

Oh wow, imagine a diagram, like an engine schematic, left to right, drawing knowledge of the compiled code AND the Unity assets…

Both inside Unity on each field OR available by right-clicking on the identifier in code:

Right click → See Initialization Sequence

And then you could instantly tell… “Oh, it’s the prefab OVERRIDE that’s doing it!”

Oh yes: then warnings if something changes underneath an overridden value: new initializer… new base setting… it would have to be a pretty crazy complex diagram to truly capture all choices…

I know you can edit Visual studio styles rules, but I doubt you can tell it to warn you about inline initialisation. It’s a pretty fundamental feature of the language.

You can only inline initialise with static or constant values. Which is why you probably see some things initialised in Awake/Start/OnEnable, because they can’t be initialised inline. Or the initialisation depends on something else being initialise first.

private int _someInt = 5; // can initialise inline

private Rigidbody _rigidbody; // can't initialise inline

private void Awake()
{
    _rigidbody = this.GetComponent<Rigidbody>();
}

In serialised values the inline initialise is used only once to set the initial value. Anything after that is whatever value you set in the inspector. Changing the value of the inline initialised value won’t change any instances already serialised.

On non-serialized fields, it will be used to reset the the field to the inline initialised value when the asset is reserialised (ergo, during a domain reload).

And it gets pretty hairy when you think about it:

public int foo = 1;

void Reset()
{
    foo = 2;
}

Now I put that on a prefab and set foo to 3.

Now I make a prefab override and set foo to 4.

Now I drag the prefab override into a scene and set foo to 5 and save the scene.

All of the above gets source-controlled and committed and done.

Now what happens to foo in each of these cases, considered one at a time:

I open the scene and press Reset() and save the scene.

I edit the prefab override and press Reset()

I edit the base prefab and press Reset()

And what is foo if I simply use .AddComponent() to add my MonoBehaviour?

etc.

“Stay calm and attach the debugger.”

Yeah that would be quite useful. A little more than I need but could be a great tool.

Ok - a little bummed the answer is the debugger.

If anyone has experience with writing custom static code analysis in Visual Studio, looking forward to hearing a response. I could imagine many small (but annoying) problems being solved with simple static analyses.