Open2

enumを詳しく調べる

muranakarmuranakar

1. enumの基本概念

1.1 enumとは

enumは固定の定数値を表現するための特別なクラスです。

enum Status {
  active,
  inactive,
  pending
}

注意点:

  • enumの各値は大文字から始めることも可能ですが、Dartのスタイルガイドでは小文字を推奨しています
  • コンマの後にスペースを入れるのが推奨された書き方です
  • 最後の要素の後にもカンマを付けることができます(trailing comma)

1.2 enumの特徴

enum Status {
  active,
  inactive,
  pending,  // trailing comma
}

void main() {
  final status = Status.active;
  print(status);  // 出力: Status.active
}

重要な特徴:

  1. 自動的にEnumクラスを継承します
  2. シールドされており、継承できません
  3. インスタンス化できません
  4. 定数値として扱われます

1.3 基本的なプロパティ

1.3.1 index

各値は自動的に0から始まるインデックスを持ちます。

void main() {
  print(Status.active.index);   // 出力: 0
  print(Status.inactive.index); // 出力: 1
  print(Status.pending.index);  // 出力: 2
}

注意点:

  • インデックスは変更できません
  • 値の順序を変更するとインデックスも変更されます
  • インデックスに依存したロジックは避けるべきです

1.3.2 name

各値は文字列としての名前を持ちます。

void main() {
  print(Status.active.name);   // 出力: "active"
  print(Status.inactive.name); // 出力: "inactive"
}

注意点:

  • nameプロパティは変更できません
  • 文字列比較よりもenum値の直接比較を推奨します

2. 拡張されたenum(Enhanced Enum)

2.1 フィールドの追加

enum Device {
  smartphone(screenSize: 5.8, hasCamera: true),
  tablet(screenSize: 10.1, hasCamera: true),
  desktop(screenSize: 27.0, hasCamera: false);

  const Device({
    required this.screenSize,
    required this.hasCamera,
  });

  final double screenSize;
  final bool hasCamera;
}

注意点:

  • すべてのフィールドはfinalでなければなりません
  • コンストラクタはconstである必要があります
  • 必須パラメータにはrequiredキーワードを使用します

2.2 メソッドの追加

enum Device {
  smartphone(screenSize: 5.8, hasCamera: true),
  tablet(screenSize: 10.1, hasCamera: true),
  desktop(screenSize: 27.0, hasCamera: false);

  const Device({
    required this.screenSize,
    required this.hasCamera,
  });

  final double screenSize;
  final bool hasCamera;

  // ゲッター
  bool get isPortable => this != Device.desktop;

  // メソッド
  String getDescription() {
    return 'Device with ${screenSize}inch screen'
           '${hasCamera ? ' and camera' : ''}';
  }
}

注意点:

  • メソッド内でthisを使用できます
  • オーバーライド禁止のメソッドがあります(index, hashCode, ==など)
  • メソッドは各enumインスタンスで同じ動作をする必要があります

2.3 インターフェースの実装

enum Priority implements Comparable<Priority> {
  low(value: 0),
  medium(value: 1),
  high(value: 2);

  const Priority({required this.value});
  final int value;

  
  int compareTo(Priority other) => value.compareTo(other.value);
}

注意点:

  • 複数のインターフェースを実装できます
  • すべての抽象メソッドを実装する必要があります
  • mixinは使用できません

3. 実践的な使用方法

3.1 switch文での使用

enum ConnectionStatus {
  connected,
  disconnected,
  waiting
}

void handleConnection(ConnectionStatus status) {
  switch (status) {
    case ConnectionStatus.connected:
      print('接続されました');
      break;
    case ConnectionStatus.disconnected:
      print('切断されました');
      break;
    case ConnectionStatus.waiting:
      print('接続待ち');
      break;
  }
}

注意点:

  • すべてのケースを網羅する必要があります
  • defaultケースを使用する場合は、将来の拡張性を考慮してください
  • breakを忘れないようにしましょう

3.2 値の検証

enum PaymentType {
  creditCard,
  debitCard,
  cash;

  bool get isCard => 
    this == PaymentType.creditCard || 
    this == PaymentType.debitCard;
}

注意点:

  • 複雑な条件分岐はメソッドやゲッターにまとめることで可読性が向上します
  • 値の検証は型安全な方法で行うべきです

3.3 Map/Listでの使用

enum UserRole {
  admin,
  user,
  guest;

  static final Map<UserRole, String> permissions = {
    UserRole.admin: 'full',
    UserRole.user: 'limited',
    UserRole.guest: 'readonly',
  };

  String get permission => permissions[this] ?? 'none';
}

注意点:

  • Mapのキーとしてenumを使用する場合、すべての値をカバーすることを推奨します
  • 静的な(static)マップを使用する場合はfinalにすることを推奨します

4. パフォーマンスと最適化

4.1 メモリ使用

enum CacheStrategy {
  memory,
  disk,
  network;

  // 静的なマップは一度だけ作成されます
  static final Map<CacheStrategy, int> _timeouts = {
    CacheStrategy.memory: 60,
    CacheStrategy.disk: 3600,
    CacheStrategy.network: 0,
  };

  int get timeout => _timeouts[this]!;
}

注意点:

  • enumはシングルトンとして実装されます
  • 静的なデータ構造は慎重に使用してください
  • メモリ使用量を考慮してフィールドを設計してください

4.2 比較とパフォーマンス

enum Size {
  small,
  medium,
  large
}

// 推奨される比較方法
void compare(Size size) {
  if (size == Size.small) {  // 直接比較
    // ...
  }
}

// 避けるべき比較方法
void compareString(Size size) {
  if (size.toString() == 'Size.small') {  // 文字列比較は避ける
    // ...
  }
}

注意点:

  • enum値の直接比較は高速です
  • 文字列比較は避けてください
  • indexによる比較は避けてください

5. エラー処理とバリデーション

5.1 値の検証

enum ValidationStatus {
  valid,
  invalid,
  pending;

  bool get isValid => this == ValidationStatus.valid;

  void validateOrThrow() {
    if (this == ValidationStatus.invalid) {
      throw Exception('Validation failed');
    }
  }
}

注意点:

  • 無効な状態の処理を明確に定義してください
  • 例外を投げる場合は適切な例外クラスを使用してください
  • バリデーションロジックは集中管理してください

5.2 安全な型変換

enum LogLevel {
  debug,
  info,
  warning,
  error;

  static LogLevel fromString(String value) {
    try {
      return LogLevel.values.firstWhere(
        (level) => level.name == value.toLowerCase()
      );
    } catch (e) {
      return LogLevel.info;  // デフォルト値を返す
    }
  }
}

注意点:

  • 文字列からの変換は常に検証が必要です
  • デフォルト値を適切に設定してください
  • 変換失敗時の挙動を明確に定義してください