Enums
A Kotlin enum class becomes a C# enum with matching ordinal values. A property or function declared on the enum class becomes a C# extension method, since a C# enum can't carry behavior itself. A companion object's functions and properties become statics beside them; see Functions and companion members.
A getter that throws surfaces as a catchable, mapped exception rather than crashing the process, the same as any other property getter. A var binds a setter too, SetX(this Mood mood, value), alongside the getter:
A property's own name PascalCases the same way any other bridged property does, whatever its casing or type: a camelCase constructor property (displayName) binds as DisplayName(), and a Boolean name keeps a leading is rather than dropping it (isCuddly binds as IsCuddly(), not Cuddly()).
An entry's C# name is predictable from its Kotlin spelling alone: split the entry name on _, and a segment with a lowercase letter in it keeps its own casing (only its first character is capitalized); a segment with no lowercase letter (all caps, digits) is treated as one word and PascalCased as before. A const val, wherever it's declared, follows the same rule.
Kotlin entry | C# member |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AB1C stays Ab1c: an all-caps segment with a digit in it can't be told apart from an ordinary all-caps segment (HAPPY) by any per-character rule, and preserving it would mean also preserving HAPPY verbatim. Two entries whose C# names collide after this conversion (for example FOO_BAR and FooBar) fail the build with ERROR_CSHARP_NAME_COLLISION, naming both; rename one of them.
A property or method typed with the enum, in any parameter, return, or getter/setter position, binds like any other enum-typed member: var mood: Mood becomes a settable Mood property. This also covers a member whose own type is a different enum, such as enum class Swirl(val patch: Patch): swirl.Patch() returns the C# Patch enum, not a raw handle.
An enum member property follows the same type rules as any other property: a class-typed member returns an owned wrapper the caller must dispose (using var toy = mood.FavouriteToy();), an Int?-shaped member reads through the usual nullable pair, and a member typed with something a property can't cross (a lambda, for example) is a named skip rather than a broken export.
Nullable
A bare Mood? (not wrapped in a value class) binds as C# Mood? at every ordinary position: property, constructor/method/extension/top-level parameter, and return. null stays distinct from every ordinal, including Mood.Happy at ordinal 0.
A property getter returning Mood? is evaluated twice when it returns a value. Keep those getters free of side effects and ensure their result stays stable between evaluations. A top-level function returning Mood?, and ordinary instance methods such as Soothe above, evaluate once.
As a collection component
A bare enum crosses a List/Map/Set element, key, or value (nullable included) as its int ordinal, both ways.
This also makes a bare-enum collection property settable, not get-only; see Collections: Mutable collection properties. A nested collection element (List<List<Mood>>) has no representation and is skipped; see Collections.
As an enum-bounded type argument
A Kotlin T : Enum<T> binds as where T : struct, global::System.Enum, so any generated enum is a valid type argument: new Rosette<Medal>(Medal.Gold). See Generics: An enum bound.
Nested enums
An enum class nested inside an admitted owner (a class, object, or interface, at any depth, generic or not; see Classes and objects: Owners ADR-134 admits) is declared as a real nested C# enum, Outer.Kind; under a generic Outer<T> it sits on a non-generic holder beside it, still spelled Outer.Kind. Its extension methods are hoisted to a top-level class instead (OuterKindExtensions, named for the whole enclosing chain), because C# forbids an extension method inside a nested class (CS1109).
Name a property that holds the nested enum something other than the enum's own name (setting, not mode): PascalCasing a mode property to Mode would collide with the nested type Mode itself (CS0102).
A nested enum under a still-deferred owner (another enum class or a generic interface; see Classes and objects: Nested types) is not declared: the declaration itself is skipped with SKIPPED_NESTED_DECLARATION, and a parameter, return, or property typed with it is skipped with SKIPPED_UNSUPPORTED_TYPE/SKIPPED_UNSUPPORTED_PROPERTY naming UNDECLARED_ENUM; the owning class still generates with its other members.
Functions and companion members
A function declared in the enum class body binds as an extension method, the same way a property does. An abstract fun with a body on each entry dispatches to the entry's own body:
The receiver parameter is named after the enum (mood, chatter), or receiver if the function already has a parameter of that name. Overloads, default arguments and a throwing function (a catchable mapped exception) behave as they do on a class. A class-typed return is a fresh wrapper you must Dispose.
A function in the enum's companion object binds as a plain static method, and a companion val or var as a static property, both on the same {Enum}Extensions class. A C# enum cannot declare statics, and the generated code's language version has no static extension members, so there is no Mood.Fallback():
An enum with only functions still gets its {Enum}Extensions class.
A Flow, StateFlow, SharedFlow, MutableStateFlow or MutableSharedFlow property (in the constructor, in the body, or an abstract val each entry overrides) binds the same way, as an extension property such as Tail.Oreo.Height. Each entry keeps its own storage. See Flow on an enum.
What is not bound
A generic or lambda-returning enum member function is skipped with a named diagnostic. Expose it as a top-level function taking the enum as a receiver or parameter instead. A
suspendorFlowmember binds; see Coroutines and Flow.A companion
const valis skipped (SKIPPED_UNSUPPORTED_PROPERTY). Use a top-levelconst val.Two declarations that spell the same C# signature in
{Enum}Extensionsfail generation withERROR_CSHARP_SIGNATURE_COLLISION: a member function beside a same-named member property (fun description()besideval description), or a companionvalbeside any same-named member. Rename one.
A member extension declared inside an enum binds as an extension method on the enum with the receiver as an extra argument (Drowse.Deep.GetSnooze(loafer)); on the suspend and Flow forms that extra argument is named receiver_. See Member extensions.
C# enums in Kotlin
A bound C# enum becomes a Kotlin enum class whose entries are SCREAMING_SNAKE_CASE. An acronym run stays one word, and a digit starts a new one:
If two members of one enum would get the same entry name (HTTPStatus and HttpStatus both give HTTP_STATUS), each of them keeps its C# spelling as the Kotlin entry and the build logs one INFO_ENUM_ENTRY_KEPT_VERBATIM note per member. The enum still binds in full.