How can I access static members in a generic context?

Viewed 75

I need a workaround or idiomatic way to access the static members defined in some type from a generic context.

Example:

enum E { first, second, third }

// no direct syntax to constrain to enum types
class EnumKeyList<TEnum> {
  List<Object> _values;

  // unable to access static member
  EnumKeyList() : _values = List.filled(TEnum.values.length, Object());

  // unable to access instance member
  Object operator [](TEnum entry) => _values[entry.index];
}

Usage:

final list = EnumKeyList<E>(); // E.values.length would provide implicit fixed-size list instantiation
list[E.first] = 5; // can use enumeration entries as keys
  • I want to avoid the overhead of Map (hashing and additional memory). The real use case must index into the list in tight loops.
  • Having a fixed set of named keys is a useful requirement, but the example EnumKeyList should work with any generic type argument that provides an enumeration like interface.
  • Using enumerations provides the shortest way to declare valid 0-indexed keys and the count of the amount of entries through an enumeration's static values member.

Swift enumerations and protocols allow for static members. C# has constraints for enumeration types. C++ generics dwarf everything. Is there a simple way to achieve this in Dart?

I realize that I can declare my own class instead of an enumeration, but then I lose the implicitly generated members (having to manually assign a value to each constant in the class (bad for maintenance)) and I still can't provide access to a static member from the generic context.

See here for examples of how unmaintainable this is:

abstract class Enum {
  final int rawValue;

  const Enum(this.rawValue) : assert(rawValue >= 0);

  // don't bother with a static 'values' member
}

class E extends Enum {
  const E(int rawValue) : super(rawValue);

  static const first = E(0);
  static const second = E(1);
  static const third = E(1); // repeated values

  static const List<E> values = <E>[first, second]; // missed one
}
1 Answers

You cannot access static members through type variables.

Dart static members are really just declared in the namespace of the corresponding class/mixin/extension declaration, they are not part of the type. Type variables hold types, not declarations.

There is no idiomatic workaround.

You have to figure out which operations you need your class to support, then you can introduce a strategy object representing the class, and pass that to the function instead of (or alongside) the type argument.

In this case, you probably want the EnumKeyList constructor to take the list of values as an argument, so:

EnumKeyList(List<T> values) : _values = List.unmodifiable(values);

The workaround, in general, is to pass the values you'd want to read from a static member directly to the function needing them, along with the type. You can't access them using the type alone.

The "cannot access index" problem could be fixed by the language adding an interface to all enums, like abstract class Enum { int get index; } and make all enum classes implement that interface. There is no easy way to allow access to the values knowing only the type. It might be possible to do something magical in the compiler and platform libraries, but it won't extend to user-written enums like this, and no viable way to emulate it.

Related