Skip to content

First iteration of nullable approach.#61

Open
horfius wants to merge 7 commits intoinfernoplus:mainfrom
horfius:horfius/NullableFirstIteration
Open

First iteration of nullable approach.#61
horfius wants to merge 7 commits intoinfernoplus:mainfrom
horfius:horfius/NullableFirstIteration

Conversation

@horfius
Copy link
Collaborator

@horfius horfius commented Jan 16, 2026

First iteration as enabling nullable at the project level created 1300 warnings. Therefore, I'm enabling it locally to find nullable issues and work through them in chunks. I enable it in the individual files from there and disable it at the project level so as not to destroy everyone's build outputs with warnings.

The general approach here is to throw informative exceptions where null/missing data cannot be recovered. There may be some instances, specifically with arrays, where I put empty arrays as the default, but it possibly would be better to throw exceptions instead.

{
case Type.Main:
mainOutput.Add(message); break;
mainOutput?.Add(message); break;
Copy link
Owner

@infernoplus infernoplus Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of wonder why we need nullable checks in classes like this. It should be impossible for this to be null here unless someone added code above the Lort.Init() in main.

I guess there is no reason not to do this either as it has no downside. IDK just thinking out loud.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because the compiler isn't that smart, unfortunately. You're right in that unless that call is missed or royally screws up in an unexpected manner, it should never be null. Unless someone foolishly added something that happens prior to that call from Main, which is really what this would cover.

In reality, this is 99.999% superfluous but it gets rid of the compiler warning.


public readonly string job, faction; // class is job, cant used reserved word
public string job { get; init; } // class is job, cant used reserved word
public string? faction { get; init; }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the differences between get; init; and readonly ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IDE hints make it easier to track usage. The real answer is it does technically have a difference in the styles of initializing the property, with init being a little bit more flexible. It's not a common situation, so it's mostly the IDE hints for figuring out usage of properties. https://stackoverflow.com/questions/62372422/what-is-difference-between-init-only-and-readonly-in-c-sharp-9 for reference

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One limitation of readonly I don't like is that when you have a subclass with it's own constructor you can't set readonly values of the parent class in that subclass constructor. That possible with init or the same issue?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is allowed, even if you call the base constructor:
image

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oooo I could improve some things in a few places with that. Nice.


essential = record.json["npc_flags"] != null ? record.json["npc_flags"].GetValue<string>().ToLower().Contains("essential") : false;
essential = record.json["npc_flags"]?.GetValue<string>().ToLower().Contains("essential") ?? false;

Copy link
Owner

@infernoplus infernoplus Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good cleanup here. Been meaning to move all my parsing over to GetValue()
Thanks!

int g = int.Parse(record.json["data"]["color"][1].ToString());
int b = int.Parse(record.json["data"]["color"][2].ToString());
int a = int.Parse(record.json["data"]["color"][3].ToString());
int r = int.Parse(record.json["data"]?["color"]?[0]?.ToString() ?? throw new Exception("LightContent is missing value data->color->r!"));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With stuff like this which is guaranteed to never be null I kind of wonder if it's needed to have checks?

Like I get wanting them for consistency but it does seems superfluous. Fields like this in the ESM are rigidly required and so the ESM json will always have it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes total sense and this is just one of those areas where I'm not familiar enough with the input data structures to know if this should always be present. This approach will very quickly notify if there is an invalid input, but we could just as easily do ! instead of ? for null imperatives which will force the compiler to assume it won't be null (it can realistically still be null).

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aight. If you want to you could update the PR and change any ESM json parsing to use ! instead. The differentiation is pretty easy here. Anything where I do not explicitly check null already is a required field in the ESM. I can clarify anything you are unsure about as well.

.WithDegreeOfParallelism(Const.THREAD_COUNT)
.Select(node => {
Cell cell = MakeCell(node, esm);
var cell = MakeCell(node, esm);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's weird but the use of var like this drives me crazy.

I think it's PTSD from working as JS programmer for too long.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get it, the reason is that technically MakeCell returns Cell? not Cell so with nullable enabled it causes compilation warnings. I could just write Cell? instead. At least compared to JS the compiler and IDE do know the type so none of the intellisense/IDE hints should be wrong or fuzzy.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I think for me it's a bit of a "stuck in my ways" kind of complaint. I just find readability is better with explicit types. I still use them sometime when writing out the type becomes a hassle like with KeyValuePair objects though haha

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I totally get it. If not for the IDE it can be a bit confusing, so reviewing a PR can get messier with them. I was being lazy so I may change it or I may not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants