I believe it is a bit-field, but probably not what you would call "simple".
A Delphi set may have up to 256 elements, or elements with values up to 255, which may not necessarily be the same thing since enum members can be assigned specific values and are not necessarily contiguous, e.g :
TEnum = (a, b, c=255);
TSet = set of TEnum;
Results in a TSet with a possible maximum size of 256 bits even tho it has only 3 possible members. (and note that the member "a" has the value 0, NOT 1!)
You can see this if you use the sizeof() function on your set type or a variable of that type, which will indicate how many bytes of storage that type or variable occupies.
TEnum = (a, b, c=255);
TSet = set of TEnum;
>>> sizeof(TSet) = 32
TEnum = (a, b, c);
TSet = set of TEnum;
>>> sizeof(TSet) = 1
Any mechanism you devise that relies on the internal storage of a Delphi set type is going to be fragile and will require that you have enum types defined in both C# and Delphi that match but which cannot easily/reliably be identified as having fallen out of synch.
If that is a practical concern then I'd suggest that you instead pass your values as an array of enum member names and use RTTI in Delphi to rebuild the set on the Delphi side by converting the enum names to the corresponding enum values and adding them to your set as required (if an invalid enum member name is specified -1 is returned as the enum value from GetEnumValue):
enumValue := TEnum( GetEnumValue(TypeInfo(TEnum), sEnumMemberName) );
if Ord(enumValue) = -1 then
raise Exception.Create('Invalid enum value' + sEnumMemberName);
Include(setVar, enumValue);
GetEnumValue() and the corresponding GetEnumName() functions are part of the TypInfo unit.
In this way you will be able to detect and handle a situation where the C# code has specified an enum value (by name) that the Delphi code does not recognise (or vice versa of course), in a way that is perhaps not so reliable when making assumptions about anonymous bits in an arbitrary n-byte chunk of storage.
You do of course have to still ensure that both your C# and Delphi code are using the same, or easily mapped, names for the enum members.
A compromise might be to pass the values as a simple array of bytes = each member of an enum has an underlying byte-sized ordinal value, so as long as your enum members in C# have the same underlying ordinal value as your enum members in Delphi you can simply cast them as required, e.g, in very rough pseudocode that you should be able to adapt quite easily:
given:
enumValues: array of Byte;
setVar: TSet; // set of TEnum
for i := 0 to Length(enumValues) do
Include(setVar, TEnum(enumValues[i]));