Things to know about FName
FName includes an optional instance number
Most FName in UE don’t contain numbers, but an optional number IS
supported as a part of the FName.
To help visualize this, here’s a table with some examples:
| Expression | PlainName | Number | ToString() |
|---|---|---|---|
FName("Foo") |
"Foo" |
0 |
"Foo" |
FName("Foo", 0) |
"Foo" |
0 |
"Foo" |
FName("Foo_0") |
"Foo" |
1 |
"Foo_0" |
FName("Foo", 1) |
"Foo" |
1 |
"Foo_0" |
FName("Foo_1") |
"Foo" |
2 |
"Foo_1" |
FName("Foo", 2) |
"Foo" |
2 |
"Foo_1" |
Significantly, notice that the internal Number is always 1 greater
than the number represented in the string.
This allows for FName("Foo") to be functionally equivalent
to FName("Foo", 0) and distinctly different
from FName("Foo_0").
The operator== considers both the PlainString part of the
FName and the Number part of the name, such that
FName("Foo", 1) != FName("Foo", 2)
If you want to compare only the PlainString part of the name,
such that FName("Foo", 1) will be considered equivalent to FName("Foo", 2),
the most efficient way to do so is to use GetComparisonIndex(), like:
if (FName("Foo_1").ComparisonIndex() == FName("Foo_2").ComparisonIndex())
{
// This is true.
// "Foo" == "Foo"; the "_1" and "_2" are ignored by ComparisonIndex()
}
Sample code
Run this code to help understand how FName handles the optional Number data:
FName Foo ("Foo"); // same as FName("Foo", 0)
FName Foo1 ("Foo", 1); // same as FName("Foo_0")
FName Foo2 ("Foo_1"); // same as FName("Foo", 2)
UE_LOG(LogTemp, Log, TEXT("Foo PlainString=\"%s\", Number=%d, ToString=\"%s\""), *Foo.GetPlainNameString(), Foo.GetNumber(), *Foo.ToString());
UE_LOG(LogTemp, Log, TEXT("Foo1 PlainString=\"%s\", Number=%d, ToString=\"%s\""), *Foo1.GetPlainNameString(), Foo1.GetNumber(), *Foo1.ToString());
UE_LOG(LogTemp, Log, TEXT("Foo2 PlainString=\"%s\", Number=%d, ToString=\"%s\""), *Foo2.GetPlainNameString(), Foo2.GetNumber(), *Foo2.ToString());
// All these comparison methods include both the PlainString AND the Number
UE_LOG(LogTemp, Log, TEXT("Foo == Foo1 ? %s"), Foo == Foo1 ? TEXT("true") : TEXT("false"));
// Optimal way to compare JUST the PlainString part, ignoring the Number part
UE_LOG(LogTemp, Log, TEXT("Foo.GetComparisonIndex() == Foo1.GetComparisonIndex() ? %s"), Foo.GetComparisonIndex() == Foo1.GetComparisonIndex() ? TEXT("true") : TEXT("false"));
Result
LogTemp: Foo PlainString="Foo", Number=0, ToString="Foo"
LogTemp: Foo1 PlainString="Foo", Number=1, ToString="Foo_0"
LogTemp: Foo2 PlainString="Foo", Number=2, ToString="Foo_1"
LogTemp: Foo == Foo1 ? false
LogTemp: Foo.GetComparisonIndex() == Foo1.GetComparisonIndex() ? true
Thanks!
Thanks to Dirtsleeper on the Unreal Source Discord for bringing this
non-obvious behavior to my attention.