web-dev-qa-db-ja.com

フラグ列挙型とビット単位の操作と「ビット列」の比較

仲間の開発者は、選択した曜日を1と0の7文字の文字列として保存することを提案しました。つまり、月曜日と金曜日は「1000100」です。私はFlags列挙型とビット単位の操作を含むソリューションを推奨しました(強くお勧めします)。これは、これを行うためのよりクリーンな方法であり、他の開発者にとって理解しやすいはずです。

  [Flags()]
  public enum Weekdays : int
  {
    Monday = 1,
    Tuesday = 2,
    Wednesday = 4,
    Thursday = 8,
    Friday = 16,
    Saturday = 32,
    Sunday = 64
  }

しかし、サンプルソリューションの実装を開始したとき、結局のところ、単純な文字列アプローチの方が簡単であることに気づきました。データを見ているだけの場合、ビット文字列は「17」よりも明白です。また、C#のビット単位の操作は直感に反し、非常に冗長であることがわかります。

Weekdays workDays = Weekdays.Monday | Weekdays.Tuesday;
if ((workDays & Weekdays.Monday) == Weekdays.Monday) 
{...}

もちろん、これを拡張メソッドにうまく組み込むことができますが、突然、少なくとも文字列ソリューションと同じ数のコード行ができてしまい、ビット単位のコードの方が読みやすいとは言えません。

そうは言っても、私はまだフラグ列挙型とビット単位の演算を使用します。私が考えることができる主な利点は

  • よりよい性能
  • ストレージに必要なスペースが少ない

では、ビットワイズソリューションを同僚に販売するにはどうすればよいですか?したほうがいい?文字列に対してこのメ​​ソッドを使用する他の利点は何ですか?サンプルプロジェクトを完了した後も、チームは文字列ベースのソリューションを選択したことに気付きました。もっと良い/強い議論が必要です。単純なビット文字列ではなくフラグ列挙型を使用する必要があるのはなぜですか?

35
Jakob Gade

フラグ列挙型を使用する利点:

Flags enumの使用の欠点:

  • 理解しにくい人間のためのデータ表現(たとえば、17にはどのフラグが設定されていますか?)


ビットの文字列を使用する利点:

  • プログラマが文字列に設定されているビットを簡単に確認できる

ビットの文字列を使用することの否定:

  • 非標準的なアプローチ
  • 設計に不慣れなプログラマにとって理解しにくい
  • 「ガベージ」値を設定する可能性が高い(例:stringValue = "Sunday")
  • 不要な文字列の作成
  • 不要な文字列解析
  • 追加の開発作業
  • 車輪の再発明(丸い車輪でさえも)


ビットの文字列を見て、何が設定されているかを確認できることが本当に重要ですか? 17が月曜日と金曜日であることを理解するのが難しい場合は、いつでも電卓を使用してバイナリに変換できます。または、「表示」(またはデバッグ)で使用するための何らかの文字列表現を追加します。 そんなに難しいことではありません


また、ビットの文字列を確実に近づけようとする場合は、かなりのカプセル化を行って、Flags列挙型がすでに提供している抽象化のレベルに引き上げる必要があるようです。アプローチが単にビットのストリングを直接操作することである場合、それは読みにくく(理解しにくく)、おそらくエラーが発生しやすくなります。

例えばあなたはこれを見ることになるかもしれません:

days = "1000101"; // fixed bug where days were incorrectly set to "1010001"
41

標準データ構造(この場合は、DayOfWeek組み込み列挙型)を置き換えるために非標準データ構造を作成するべきではありません。代わりに、既存の構造を拡張します。これは、あなたが話していたビットフラグメソッドと本質的に同じように機能します。

namespace ExtensionMethods
{
    public static class Extensions
    {
        /*
         * Since this is marked const, the actual calculation part will happen at
         * compile time rather than at runtime.  This gives you some code clarity
         * without a performance penalty.
         */
        private const uint weekdayBitMask =
            1 << Monday 
            | 1 << Tuesday
            | 1 << Wednesday
            | 1 << Thursday
            | 1 << Friday;
        public static bool isWeekday(this DayOfWeek dayOfWeek)
        {
            return 1 << dayOfWeek & weekdayBitMask > 0;
        }
    }   
}

これで、次のことができます。

Thursday.isWeekday(); // true
Saturday.isWeekday(); // false
23
Imagist

平日の組み合わせができる授業を作る。クラス内では、どちらの方法でもデータを表すことができますが、文字列ではなくフラグ列挙型を使用します。クラスの外では、列挙値を使用するだけで、実際のロジックはクラスにカプセル化されます。

何かのようなもの:

[Flags]
public enum Days {
   Monday = 1,
   Tuesday = 2,
   Wednesday = 4,
   Thursday = 8,
   Friday = 16,
   Saturday = 32,
   Sunday = 64,
   MondayToFriday = 31,
   All = 127,
   None = 0
}

public class Weekdays {

   private Days _days;

   public Weekdays(params Days[] daysInput) {
      _days = Days.None;
      foreach (Days d in daysInput) {
         _days |= d;
      }
   }

   public bool Contains(Days daysMask) {
      return (_days & daysMask) == daysMask;
   }

   public bool Contains(params Days[] daysMasks) {
      Days mask = Days.None;
      foreach (Days d in daysMasks) {
         mask |= d;
      }
      return (_days & mask) == mask;
   }

}

使用例:

Weekdays workdays = new Weekdays(Days.MondayToFriday);
if (workdays.Contains(Days.Monday, Days.Wednesday)) {
   ...
}
6
Guffa

問題は、人間の目がこの格納された値を実際に目にするかどうかに集中する必要があります。もしそうなら、やや人間が読める形式が明らかに重要です(もしそうであれば、実際の曜日名の配列のような、もっと大きなものについても議論します)。

ただし、少なくとも私がこれまでに作成したすべてのアプリでは、この種のデータはどこかにある小さなフィールドに入り、c#コードを除いて二度と表示されません。読み取り可能コード内。あなたの同僚は、ビット単位の演算の組み込み40年以上使用のアイデアを使用する代わりに、0と1を値にマップする文字列パーサーを本当に書きたいと思っていますか?

1
Rex M

面白いことに、これらの方法はどちらもまったく同じです。 flagsメソッドのみがより明確です。

個人的にはフラグを使用します(モデルによっては、リストを保持している人に対してリストとして保存する方がよい場合もあります)。

-編集

明確にするために、パフォーマンスは実際にあなたがやっていることを考慮する必要はないと思います。したがって、最も読みやすいものを使用してください。 (私見、これは名前付きフラグです)。

0
Noon Silk

Flagsメソッドは慣用的です(つまり、経験豊富なプログラマーが行うことであり、少なくともC/C++/C#言語で表示および実行に慣れています)。

0
ChrisW