- Home /
What's the best way to implement a "set" data Type in C#?
Hi Unity Answers Community,
I was wondering, (may be answered before but couldn't find the answer), is there a "set" data type in Unity's C#?
That is, an enum which elements could be present or not in a single time (e.g: in a game, the enemy's immunities: fire, water and death)
something that could be easily implementable in the Inspector, much like the Culling mask of a Camera.
I was dreaming of a syntax like:
public set ImmunitiesSet of {
Fire,
Ice,
Holy
}
that automagically shows up properly in the inspector and allows Mask-like operations.
Since I'm pretty sure there isn't a native solution, in your experience what's the most straightforward way to implement this, in the most general case?
Thanks for your time.
Enum all the way. having a strongly typed list is nice although i dont think it can contain spaces. might just use a dictionary enum, string
Answer by Bunny83 · Mar 28, 2014 at 02:11 AM
Sure, you can use enums as bitmask, however you're limited to 32 values of course. Also you should use hex numbers to specify the bitmask values, it's less error prone ;)
public enum SomeBitMask
{
Value1 = 0x0001,
Value2 = 0x0002,
Value3 = 0x0004,
Value4 = 0x0008,
Value5 = 0x0010,
Value6 = 0x0020,
Value7 = 0x0040,
// ...
}
To display a bitmask dropdown list in the inspector you can use this property drawer i've written.
Looks like a nice solution, I'll take some time to study it.
I was also wondering if changing the enum element types to string would be a nice move (to allow an infinite number of elements) or not. Or maybe a hashmap. I fear to overcomplicate this, this really should have been a native data structure.
@NeatWolf:
The great thing about bitmasks is that they are fast and don't require much memory (just one "int" [4 bytes]).
Enums can only use an integral type as underlying type, so a string doesn't work, especially since a string isn't a value-type and doesn't have bit-operator support ;)
You could boost the bitmask up to 64 values by using ulong / int64 as underlying type but you won't get any further than that. If you need more than 32 / 64 you might want to use the HashSet type. It's of course slower and requires more memory but does support "unlimited" elements.
ps:
If the question is answered, could you accept one of the answers to close the question?
Answer by gfoot · Mar 28, 2014 at 01:08 AM
Depending on the use case you could use a bit field, combining emum values together, but it's not particularly well-supported by the language.
It is far clearer to just use a class containing a few public bool members.
public class Immunities
{
public bool Fire;
public bool Ice;
public bool Holly;
}
Then you can add an Immunities field to your components and check the boxes in the Inspector, and the syntax for checking them from code is straightforward and readable.
This means I have to rewrite part of the code, and the inspector editor class almost each time, but I see your point.
Why do you say it's not particularly well supported? Does something weird happen to have something like
public enum Immunities
{
Fire=1,
Ice=2,
Holy=4
}
and doing some bitmasking?
I just meant that custom code to manage bitfields is more complex to write and use than if you let the language do the heavy lifting by splitting the bits into separate bools.
Compare this:
if (myImmunities & Immunity_Ice)
{
myImmunities |= Immunity_Holly;
myImmunities &= ~Immunity_Fire;
}
To this:
if (myImmunities.Ice)
{
myImmunities.Holly = true;
myImmunities.Fire = false;
}
For me, the second is much clearer to read, with less potential for error at the call site.
However, the code to check whether two instances have some flags in common is much briefer with the bit field version, because that's what bit fields are particularly good for.
If you do go with a bit field anyway (a lot of people do), you may be able to wrap it in a struct and provide properties to make it look more like the bool version. It depends a bit on the usage.
Also, whichever way you go, you shouldn't have to rewrite your editor class each time you change the elements in your class/enum - you should probably be using reflection in the editor class to automatically present whichever ones are defined (like Unity does in the default Inspector).
But the point of sets and set operations is to work on sets itself. So a union operation would be much more complicated with your "set".
How would you combine (union) those two sets:
Immunities set1 = new Immunities();
set1.Fire = true;
Immunities set2 = new Immunities();
set2.Ice = true;
set2.Holy = true;
// [...]
Immunities combined = set1 | set2;
Set operations are the msin point of using sets, right?
Sure, like many things it depends exactly what you're going to use it for. If set operations are the focus then a bit field will work better; and there's always the option of HashSet, which has a wider range of operators.
Your answer
Follow this Question
Related Questions
Is it possible to use a custom object in java (my WeaponObject) as a data type (like Vector3) 1 Answer
Creating a data type instance difference question 1 Answer
What data is automatically sent to Unity by the end user's Unity project content 1 Answer
Calabrate Accelerometer 1 Answer
When creating new elements how do you auto set values? 1 Answer