[ECS] Is it possible to check if a Component's data is not null (has been set)?

I am in a MonoBehaviour and I want to check if a component’s data has been set.

if(m_ClientWorldSimulationSystemGroup.GetSingleton().GameName != null)
{
}

results in NullReferenceException: Object reference not set to an instance of an object

Anyway if I can check if this specific value has been updated?

Which part of the given expression isn’t an instance? The system itself could be null, but we can’t really tell without a traceback. Is GameName a string, or one of the DOTS string types?

But for a struct component, the members must also be structs, so the values can be compared to the default value for that member. So typically you’d use the “default” operator: Default values of built in types - C# reference | Microsoft Learn

Checking if updated is a different question. In this case it would probably involve storing the previous component. In a SystemBase there is changed filters, but that works on a chunk level.

Thank you for the response!

.GameName is a FixedString64 (one of the DOTS string types)

The world/system are an instance as I am able to run:

m_ClientWorldSimulationSystemGroup.GetSingleton().GameName.ToString()

just fine.

It doesn’t seem like there is any default value of FixedString available, so I guess there no way to tell if a FixedString was set in ECS right now?

6748750--778189--upload_2021-1-21_8-25-29.png
It seems like the FixedString64 is just allocated memory with a null terminator. So with 0 length it would appear as a string that is == “”. Someone can have decided to make GameName == “” and this would appear as the same default value.

Seems like the only option is to take out the GameName field from ClientDataComponent and make it its own component GameNameComponent, and check if the entire component exists.

Bummer because that data goes well together but they get updated at different times in the setup flow.

I don’t think you would get that error from a FixedString, could be wrong though. If you set a breakpoint on that statement and attach the debugger it should tell you what the null reference is.

Appreciate the response Sarkahn!

I messed around with seeing if I can check if it was equal to an unset FixedString64 (by defining a new FixedString64 earlier) but found that this was ALSO equal when I actually set the value to “”.

So it appears that is un-doable…

Couldn’t you just check the .Length or .IsEmpty? Struct FixedString64 | Collections | 0.14.0-preview.16

You would THINK so, but an unset FixedString64 has a .Length of 0 and “.IsEmpty” just like a FixedString64 that is == to “” (set but empty)

Sorry if I missed something but does fixedString == default not work for what you’re trying to do?

1 Like

Sorry do you mean just the keyword default?

I actually had tried everything but that, I didn’t know that was a thing, appreciate the tip for next time! (I have implemented a new workflow)

Yeah the keyword default is what I mean. That’s typically how you check if a struct is at its default value. Of course there are some cases where a default value is a valid useful state for a struct so it’s not a catchall, but for a fixedstring I think it makes sense to treat that as a special/invalid state

1 Like

super super helpful, really appreciate it!

FYI, you would never get a```
NullReferenceException: Object reference not set to an instance of an object

So I don't think that inspecting the FixedString64 is where you want to look.

I'm new to Unity but a pretty good C# developer. So assuming that Burst/IL2CPP isn't doing something wacky that I don't know about, I would suggest you break things down into multiple lines:

```csharp
var cdp= m_ClientWorldSimulationSystemGroup.GetSingleton<ClientDataComponent>()
if(cdp.GameName != null)
{
}

honestly I bet that your m_ClientWorldSimulationSystemGroup reference is null.

That seems to make sense, but if that was the case why can I print

m_ClientWorldSimulationSystemGroup.GetSingleton().GameName.ToString()

with no error? Wouldn’t that also be an issue?

Sorry, but I didn’t catch the GameName != null of your original post. That’s not valid c# syntax (comparing a struct to null) Did you make it a nullable type? or are you sure it’s a FixedString64… If it is a FixedString64 and not a FixedString64? then you should be getting a syntax error / build failure.

It may be easier to pin down the answer if OP gave a stacktrace of this error. It may be something specific to the equality method that gets run in this situation.

yep.

Also, I’m wrong. I forgot you can add things like operator overrides, which, from looking at the FixedString64 source, they do. So yeah you can do ```
GameName != null

Ah hah, in fact, looking at the source, it looks like __ __GameName != null__ __ is what’s causing your bug!

        public unsafe static bool operator ==(in FixedString64 a, in FixedString32 b)
        {
            int aLength = a.utf8LengthInBytes;
            int bLength = b.utf8LengthInBytes;
            byte* aBytes = (byte*)UnsafeUtilityExtensions.AddressOf(in a.bytes);
            byte* bBytes = (byte*)UnsafeUtilityExtensions.AddressOf(in b.bytes);
            return UTF8ArrayUnsafeUtility.EqualsUTF8Bytes(aBytes, aLength, bBytes, bLength);
        }

Note Line 4 above. it’s trying to call NULL.utf8LengthInBytes which would throw a NullRefException.

I wrote a quick test to check

    [Test]
    [Category("ecs lowlevel")]
    public void FixedString64Null()
    {
        var fStr = new FixedString64("hello");

        if(fStr != null)
        {

        }
        else
        {
            Assert.IsTrue(false, "fStr is null?");
        }

    }

and it crashes with the same error

System.NullReferenceException : Object reference not set to an instance of an object

FixedString64Null (0.018s)

System.NullReferenceException : Object reference not set to an instance of an object

at Unity.Collections.FixedString32..ctor (System.String source) [0x00026] in C:\repos\unity\dotsman\DotsMan\Library\PackageCache\com.unity.collections@0.14.0-preview.16\Unity.Collections\FixedString.gen.cs:386
at Unity.Collections.FixedString32.op_Implicit (System.String b) [0x00000] in C:\repos\unity\dotsman\DotsMan\Library\PackageCache\com.unity.collections@0.14.0-preview.16\Unity.Collections\FixedString.gen.cs:803
at NativeCollectionTests.FixedString64Null () [0x0000d] in C:\repos\unity\dotsman\DotsMan\Assets\Scripts\Tests\NativeCollectionTests.cs:79
at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[ ],System.Exception&)
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[ ] parameters, System.Globalization.CultureInfo culture) [0x00032] in <9577ac7a62ef43179789031239ba8798>:0

:smile:

2 Likes

Okay so it is NOT the ClientSystem but I assume that leaves us with @Sarkahn_1 's recommendation of GameName != default ?

I would personally go with GameName.Length !=0 as operator overrides obfuscate the performance cost. Look at that decompiled operator ==(in FixedString64 a, in FixedString32 b) I pasted above. Not the cheapest piece of code.

Edit: that said, don’t worry about the performance. I just hate the idea of operator overrides in “performant” code I suppose.

2 Likes

I think this post will help if you want a “nullable” value type in HPC#.