数値が範囲内にあるかどうかをエレガントに確認する方法は?


157

C#と.NET 3.5 / 4でこれをエレガントにするにはどうすればよいですか?

たとえば、1〜100の数値を指定できます。

私は単純なら十分だと思います。しかし、この質問のキーワードは優雅です。これは私のおもちゃプロジェクト用であり、制作用ではありません。

この質問は速度に関するものではなく、コードの美しさに関するものでした。効率などについて話すのをやめてください。あなたが合唱団に説教していることを思い出してください。


23
再:あなたの「編集」- シンプルはエレガントです。私は個人的に、このチェックを行う非標準的な手段よりもエレガントなifステートメントを見つけました...
Reed Copsey 2010

4
「すべてを可能な限りシンプルにする必要がありますが、シンプルにする必要はありません。」
-Albert

3
@セルジオ:私は知識を持っているとは思わない。すでに単純なものを置き換えるために、言語の拡張メソッドやその他のツールを乱用することがよくあるように思います。2つのint値を比較するには何百もの方法がありますが、より明白なもの以外を使用することは、IMOの選択としては不適切です。
リードコプシー2010

3
@Sergio:私は推測し、その後、私は質問のポイントが表示されません。)
リードCopsey

6
@セルジオ:if「バロック」でない場合は修正しないでください。
StriplingWarrior

回答:


152

多くのオプションがあります:

int x = 30;
if (Enumerable.Range(1,100).Contains(x))
    //true

if (x >= 1 && x <= 100)
    //true

また、正規表現オプションについては、このSO投稿をご覧ください。


334
Enumerable.Rangeは、最初に整数の列挙可能オブジェクトを生成し、次に各アイテムをループしてそれを見つける必要があります。これはひどいアイデアであり、値のチェックと比較してパフォーマンスは大幅に異なります。LINQ Extensionsが優れているからといって、モトを採用するべきだと思います。
Matthew Abbott、


15
これはパフォーマンスの点ではひどい考えですが、OPはifステートメントよりも豪華なものを求めています。これは確かにそれを達成します...;)
Tim Coker

10
2番目のパラメーターは「停止」ではなく「カウント」であることに注意してください。したがって、たとえば、Enumerable.Range(150、300).Contains(400)はtrueを返します。
Shathur 2013

5
この回答は使用しないでください。範囲が非常に大きい場合は、恐ろしいパフォーマンスになります。@ olivier-jacot-descombesによる回答をご覧ください
アーロンヒュードン

95

もしかして?

if(number >= 1 && number <= 100)

または

bool TestRange (int numberToCheck, int bottom, int top)
{
  return (numberToCheck >= bottom && numberToCheck <= top);
}

1
そこに「is」は必要ありません...これはコンパイルされません。(それ以外の場合は、100%同意します)
リードコプシー2010

4
@ベン、私も試して特許を取得するまで待ってください:)
kemiller2002

これは最も確かな解決策だと思いますが、質問者が探しているエレガントではありませんか?
Kevin Simple

変更するのは、メソッドにstaticキーワードを追加することだけです。;-)
ロバートS.

<vs <=を可能にする境界フラグ、つまりInRange(number、lowerBound、LOWER_IS_INCLUSIVE、Upperbound、UPPER_IS_EXCLUSIVE)が必要です。私はこれをだらだらとするつもりで書いたが、私はそれについて考えた今、フラグは実際に発信者が彼らの仕様をまっすぐに得るように励ます。
ウィリアムT.マラード

56

ここでノイズを追加するために、拡張メソッドを作成できます。

public static bool IsWithin(this int value, int minimum, int maximum)
{
    return value >= minimum && value <= maximum;
}

これはあなたに何かをさせるでしょう...

int val = 15;

bool foo = val.IsWithin(5,20);

そうは言っても、チェック自体が1行だけの場合、これは愚かなことのように思えます。


1
@ベン:私は「範囲内」と言った件名を調べました(その点についてはあいまいではないと思います)が、質問の本文に「1から100の間」と書かれている(つまり、 、もちろん、あいまいです)。
Adam Robinson

48

他の人が言ったように、単純なifを使用します。

順序について考える必要があります。

例えば

1 <= x && x <= 100

より読みやすいです

x >= 1 && x <= 100

19
「より簡単」は見る人の目にあります。私は個人的に左と定数または変数に問題の変数を持ってすることを好むない右の質問インチ
Adam Robinson、

15
ではPerl 6の、あなたが書くでしょう1 <= x <= 100
ジョルダン

2
数直線の順序は最初は最も明確ですが、他の順序について目と心を訓練することができます。具体的には、常に定数を左側に配置するトリックが好きです。そうすると、の=代わりに入力したときにコンパイラーから通知されます==。等しくない関係演算子には役立ちませんが、一貫して使用することに慣れるのは簡単です。
davidbak 2016年

1
このソリューションはどのような場合でも役に立たないことを付け加えておきます。x複雑な関数呼び出しまたは時間のかかるLinq式を検討してください。この場合、これを2回実行するのは良いことではありません。確かに一時的なローカル変数に値を格納する必要がありますが、他のifまたはelse-ifが失敗した後にのみ関数を呼び出したい場合があります(else-ifステートメントなど)。一時変数では、とにかくそれらを呼び出す必要があります。(他の回答で言及されている)拡張メソッドは、それらの場合に最適なソリューションです。
ロバートS.

4
ナンバーラインの順序も好きですし、補数テストも好きです。例:x <10 || 20 <x。私には「xは10から20の範囲外です」と叫びます。
ウィリアムT.マラード

44

量産コードでは単純に次のように書きます

1 <= x && x <= 100

これは理解しやすく、非常に読みやすいです。


これは、いくつかの数学を使用して比較の数を2から1に減らす賢い方法です。アイデアは、数値が範囲外にある場合は2つの要素のいずれかが負になり、数値が境界の1つに等しい場合はゼロになるということです。

境界が包括的である場合:

(x - 1) * (100 - x) >= 0

または

(x - min) * (max - x) >= 0

境界が排他的である場合:

(x - 1) * (100 - x) > 0

または

(x - min) * (max - x) > 0

3
私の基準では、これはこれまでで最もエレガントなソリューションであり、興味深いのは、両方の式をチェックするよりもいくらか速く実行されているように見えることです。研究が行われた場合、どちらが速いか。
トーマスリンドヴァル2015年

3
最大14桁の浮動小数点数を使用して、JavaScriptでソリューションをテストし、その正確性をテストしました。これは非常に優れたコードスニペットです。できれば3回あなたに
賛成票を投じ

4
ただし、大きな正の数が含まれている場合、小さな問題がありますが、オーバーフローする可能性があります。XDコードを書くときは、そのことを覚えておきたいと思うかもしれません。
BrainStorm.exe 2016

2
質問は優雅さを求めているため、実用的な価値よりも学術的なものです。個人的には、単純な1 < x && x < 100生産的なコードを使用します。理解しやすいです。
Olivier Jacot-Descombes

1
パフォーマンスが気になる方のために、1 < x & x < 100(&&短絡なし)は、コンパイラーにx < 100、の結果に関係なく常に評価できるように指示し1 < xます。奇妙なことに(分岐予測のため)この単純な操作は、スキップするよりも常に高速です。
Tom Leys、

23

私はこれを提案します:

public static bool IsWithin<T>(this T value, T minimum, T maximum) where T : IComparable<T> {
    if (value.CompareTo(minimum) < 0)
       return false;
    if (value.CompareTo(maximum) > 0)
       return false;
    return true;
}

例:

45.IsWithin(32, 89)
true
87.2.IsWithin(87.1, 87.15)
false
87.2.IsWithin(87.1, 87.25)
true

そしてもちろん変数を使って:

myvalue.IsWithin(min, max)

読みやすく(人間の言語に近い)、同等のタイプ(整数、ダブル、カスタムタイプなど)で動作します。

開発者はコードを理解するために「頭脳サイクル」を無駄にしないため、コードを読みやすくすることは重要です。長時間のコーディングセッションでは、脳のサイクルが無駄になるため、開発者は以前に疲れてバグになりやすくなります。


3
「between」という単語を使用し、ブールフラグを使用して包括的かどうかを判断することで、さらに単純化します
Ben

良い。わかりやすいです。私はIsInRange. I'm not that keen on Ben's inclusive boolean as that requires a few more brain cycles. It has the advantage that it can be used in any class that that implements IComparer. This is in my Extensions now along with LiesWithin / LiesInside. Just can't decide which. NotOutside の名前を変更しましたが、うまくいきませんが、マイナスの条件は好きではありません
Paulustrious

21

拡張メソッドを少し悪用すると、次の「エレガントな」ソリューションが得られます。

using System;

namespace Elegant {
    public class Range {
        public int Lower { get; set; }
        public int Upper { get; set; }
    }

    public static class Ext {
        public static Range To(this int lower, int upper) {
            return new Range { Lower = lower, Upper = upper };
        }

        public static bool In(this int n, Range r) {
            return n >= r.Lower && n <= r.Upper;
        }
    }

    class Program {
        static void Main() {
            int x = 55;
            if (x.In(1.To(100)))
                Console.WriteLine("it's in range! elegantly!");
        }
    }
}

ソリューションのように!ところで、包括的なサポートを作成するにはenum Inclusive:値を使用してLowerUpperAll。そして、のために渡すIn機能タイプの1つの追加パラメータenum Inclusiveのデフォルト値とをInclusive.All更新し、Toハンドルに関数本体をAllLowerUpper値:)
ニキータ

7

これが付随的であるif場合、必要なのは単純なものだけです。これが多くの場所で発生する場合は、次の2つを検討することをお勧めします。

  • PostSharp。コンパイル後にメソッドにコードを「挿入」する属性を持つメソッドを装飾します。はっきりとはわかりませんが、これに使用できると想像できます。

何かのようなもの:

[Between("parameter", 0, 100)]
public void Foo(int parameter)
{
}
  • コード契約。コードとコードを使用する場所の静的検証により、コンパイル時に制約を確認できるという利点があります。

コード契約の場合は+1。これはパラメータの検証に固有ですが、頻繁に使用されるケースであり、静的検証は非常に役立つ可能性があります。
Dan Bryant


5

&&式を使用して2つの比較を結合することは、これを行う最もエレガントな方法です。派手な拡張メソッドなどを使用しようとすると、上限、下限、またはその両方を含めるかどうかという疑問にぶつかります。追加の変数を追加したり、何が含まれているかを示すために拡張名を変更したりすると、コードが長くなり、読みにくくなります(大多数のプログラマにとって)。さらに、比較が意味をなさない場合、Resharperなどのツールによって警告が表示されます(number > 100 && number < 1)、メソッド( 'i.IsBetween(100、1)')を使用する。

私が行う他の唯一のコメントは、例外をスローする意図で入力をチェックしている場合は、コードコントラクトの使用を検討する必要があるということです。

Contract.Requires(number > 1 && number < 100)

これはよりエレガントでif(...) throw new Exception(...)、最初に数値が境界内にあることを確認せずに誰かがメソッドを呼び出そうとすると、コンパイル時の警告が表示されることさえあります。


2
ちなみに、コントラクトスタティックアナライザーは、下限と上限の制約が別々のrequireステートメントに分割されている場合に便利です。
Dan Bryant

Dan Bryantに感謝します。まさにそれが私がここで探していたものです。Requiresおよびその他の関連するコードコントラクトメソッドの条件のスタイルに関する提案に関する多くの資料が見つかりません。
jpierson 2013

2

単純なifよりも多くのコードを記述したい場合は、次の方法が考えられます。IsBetweenという拡張メソッドを作成する

public static class NumberExtensionMethods
{
    public static bool IsBetween(this long value, long Min, long Max)
    {
        // return (value >= Min && value <= Max);
        if (value >= Min && value <= Max) return true;
        else return false;
    }
}

...

// Checks if this number is between 1 and 100.
long MyNumber = 99;
MessageBox.Show(MyNumber.IsBetween(1, 100).ToString());

補遺:実際には、コードベースで「等しいかどうかを確認するだけ」(または<、>)を行うことはほとんどありません。(最も些細な状況を除いて)純粋に例として、すべてのゲームプログラマーは基本的に、すべてのプロジェクトで次のようなカテゴリを使用します。この例では、その環境に組み込まれている関数(Mathf.Approximately)を使用していることに注意してください。実際には、通常、エンジニアリングしている状況のタイプに応じて、実数のコンピューター表現の比較が何を意味するかについて、独自の概念を慎重に開発する必要があります。(おそらく、コントローラー、PIDコントローラーなどのようなことをしている場合、問題全体が中心になり、非常に困難になることは、プロジェクトの性質になることは述べないでください。

private bool FloatLessThan(float a, float b)
    {
    if ( Mathf.Approximately(a,b) ) return false;
    if (a<b) return true;
    return false;
    }

private bool FloatLessThanZero(float a)
    {
    if ( Mathf.Approximately(a,0f) ) return false;
    if (a<0f) return true;
    return false;
    }

private bool FloatLessThanOrEqualToZero(float a)
    {
    if ( Mathf.Approximately(a,0f) ) return true;
    if (a<0f) return true;
    return false;
    }

1
ifとelseをreturn (value >= Min && value <= Max);
AeroX 2014年

比較を記述するためのエレガントな方法は、「論理的な順序で...」の場合です(Min <= value && value <= Max)。それはもっときれいです。
Fattie、2015

2
さらにこの質問については、実際のプロジェクト(特にゲームエンジニアの場合)の中心的な問題について誰も言及していないのは驚くべきことであり、近似の問題に対処する必要があります。実際のソフトウェアでは、基本的に「比較を行う」ことは決してありません(等しいか<か>かに関わらず)、状況に応じて、エラーの問題を検討して対処する必要があります。これ以上の回答は許可されていないので、私はこの回答(ここでの唯一の正しい回答!)の補遺を編集しました。
Fattie、2015

この観察と補遺に感謝します。
Tony

2

他のすべての答えは私によって発明されていないので、ここでは私の実装だけです:

public enum Range
{
    /// <summary>
    /// A range that contains all values greater than start and less than end.
    /// </summary>
    Open,
    /// <summary>
    /// A range that contains all values greater than or equal to start and less than or equal to end.
    /// </summary>
    Closed,
    /// <summary>
    /// A range that contains all values greater than or equal to start and less than end.
    /// </summary>
    OpenClosed,
    /// <summary>
    /// A range that contains all values greater than start and less than or equal to end.
    /// </summary>
    ClosedOpen
}

public static class RangeExtensions
{
    /// <summary>
    /// Checks if a value is within a range that contains all values greater than start and less than or equal to end.
    /// </summary>
    /// <param name="value">The value that should be checked.</param>
    /// <param name="start">The first value of the range to be checked.</param>
    /// <param name="end">The last value of the range to be checked.</param>
    /// <returns><c>True</c> if the value is greater than start and less than or equal to end, otherwise <c>false</c>.</returns>
    public static bool IsWithin<T>(this T value, T start, T end) where T : IComparable<T>
    {
        return IsWithin(value, start, end, Range.ClosedOpen);
    }

    /// <summary>
    /// Checks if a value is within the given range.
    /// </summary>
    /// <param name="value">The value that should be checked.</param>
    /// <param name="start">The first value of the range to be checked.</param>
    /// <param name="end">The last value of the range to be checked.</param>
    /// <param name="range">The kind of range that should be checked. Depending on the given kind of range the start end end value are either inclusive or exclusive.</param>
    /// <returns><c>True</c> if the value is within the given range, otherwise <c>false</c>.</returns>
    public static bool IsWithin<T>(this T value, T start, T end, Range range) where T : IComparable<T>
    {
        if (value == null)
            throw new ArgumentNullException(nameof(value));

        if (start == null)
            throw new ArgumentNullException(nameof(start));

        if (end == null)
            throw new ArgumentNullException(nameof(end));

        switch (range)
        {
            case Range.Open:
                return value.CompareTo(start) > 0
                       && value.CompareTo(end) < 0;
            case Range.Closed:
                return value.CompareTo(start) >= 0
                       && value.CompareTo(end) <= 0;
            case Range.OpenClosed:
                return value.CompareTo(start) > 0
                       && value.CompareTo(end) <= 0;
            case Range.ClosedOpen:
                return value.CompareTo(start) >= 0
                       && value.CompareTo(end) < 0;
            default:
                throw new ArgumentException($"Unknown parameter value {range}.", nameof(range));
        }
    }
}

その後、次のように使用できます。

var value = 5;
var start = 1;
var end = 10;

var result = value.IsWithin(start, end, Range.Closed);

2

編集:新しい回答が提供されました。この質問への最初の回答を書いたとき、私はC#の使用を開始したばかりでしたが、後から考えると、私の「解決策」は素朴で非効率的であることがわかりました。

私の元の答え:私はよりシンプルなバージョンを使います:

if(Enumerable.Range(1,100).Contains(intInQuestion)) { ...DoStuff; }

より良い方法

(少なくとも私のテストによれば)より効率的な他のソリューションを見たことがないので、もう一度試してみます。

負の範囲も機能する新しいより良い方法:

// Returns true if x is in range [min..max], else false 
bool inRange(int x, int min=1, int max=100) => ((x - max)*(x - min) <= 0);

これは、正の範囲と負の範囲の両方で使用でき、デフォルトの範囲には

1..100(両端を含む)とxチェック対象の数値として使用されmin、とで定義されたオプションの範囲が続きますmaxます。

適切な測定の例の追加

例1:

// Returns true if x is in range [min..max], else false 
bool inRange(int x, int min=1, int max=100) => ((x - max)*(x - min) <= 0);

Console.WriteLine(inRange(25));
Console.WriteLine(inRange(1));
Console.WriteLine(inRange(100));
Console.WriteLine(inRange(25, 30, 150));
Console.WriteLine(inRange(-25, -50, 0));

戻り値:

True
True
True
False
True

例2:1から150までのランダムな整数のリストを使用する

// Returns true if x is in range [min..max], else false 
bool inRange(int x, int min=1, int max=100) => ((x - max)*(x - min) <= 0);

// Generate 100000 ints between 1 and 150
var intsToCheck = new List<int>();
var randGen = new Random();
for(int i = 0; i < 100000; ++i){
    intsToCheck.Add(randGen.Next(150) + 1);
}

var counter = 0;
foreach(int n in intsToCheck) {
    if(inRange(n)) ++counter;
}

Console.WriteLine("{0} ints found in range 1..100", counter);

戻り値:

66660 ints found in range 1..100

実行時間:0.016秒


ええ、私は2013年の私の回答へのコメントにコメントしています:) @RyanTheLeach:この質問に対する私の回答は、現在「受け入れられている」回答とどう違うのですか?私はそれが最も効果的なトラバーサルではないが「ひどい」トラバースであることを理解していますか?100整数の割り当てとループがどれほど悪いのか?1950年にはおそらく社会的に受け入れられなかったが、...
cseder

@RyanTheLeach私はあなたを責めない...私は私の答えを更新したので、もっと効率的な解決策を知っているなら、詳しく説明してください!
cseder

1
コメントが表示されなくなったので、コメントを削除しました。修正してくれてありがとう。
ライアンザリーチ

1

古いお気に入りの新しいひねり:

public bool IsWithinRange(int number, int topOfRange, int bottomOfRange, bool includeBoundaries) {
    if (includeBoundaries)
        return number <= topOfRange && number >= bottomOfRange;
    return number < topOfRange && number > bottomOfRange;
}

3
実際には、包括的/包括的、包括的/排他的、排他的/包括的、排他的/排他的の4つのケースがあります。
ウィリアムT.マラード2014年

1

Cでは、時間効率が重要であり、整数オーバーフローがラップする場合は、可能ですif ((unsigned)(value-min) <= (max-min)) ...。「max」と「min」が独立変数である場合、(max-min)の追加の減算は時間を浪費しますが、その式がコンパイル時に事前計算できる場合、または実行時に一度計算して多くをテストできる場合同じ範囲に対する数値は、上記の式は、値の大部分が有効範囲未満になる場合、それはより速く使用することもできる(偶数値が範囲内にある場合に、効率的に計算することができるif ((value >= min) && (value <= max)) ...、それがするので早期終了値場合は分未満です)。

ただし、このような実装を使用する前に、ターゲットマシンのベンチマークを行ってください。一部のプロセッサでは、2つの比較は独立して実行できるため、2つの部分からなる式の方が高速な場合があります。一方、減算と比較の方法では、比較を実行する前に減算を完了する必要があります。


1

このようなものはどうですか?

if (theNumber.isBetween(low, high, IntEx.Bounds.INCLUSIVE_INCLUSIVE))
{
}

次の拡張メソッドを使用します(テスト済み):

public static class IntEx
{
    public enum Bounds 
    {
        INCLUSIVE_INCLUSIVE, 
        INCLUSIVE_EXCLUSIVE, 
        EXCLUSIVE_INCLUSIVE, 
        EXCLUSIVE_EXCLUSIVE
    }

    public static bool isBetween(this int theNumber, int low, int high, Bounds boundDef)
    {
        bool result;
        switch (boundDef)
        {
            case Bounds.INCLUSIVE_INCLUSIVE:
                result = ((low <= theNumber) && (theNumber <= high));
                break;
            case Bounds.INCLUSIVE_EXCLUSIVE:
                result = ((low <= theNumber) && (theNumber < high));
                break;
            case Bounds.EXCLUSIVE_INCLUSIVE:
                result = ((low < theNumber) && (theNumber <= high));
                break;
            case Bounds.EXCLUSIVE_EXCLUSIVE:
                result = ((low < theNumber) && (theNumber < high));
                break;
            default:
                throw new System.ArgumentException("Invalid boundary definition argument");
        }
        return result;
    }
}

1

Rangeオブジェクトを次のように実行します。

public class Range<T> where T : IComparable
{
    public T InferiorBoundary{get;private set;}
    public T SuperiorBoundary{get;private set;}

    public Range(T inferiorBoundary, T superiorBoundary)
    {
        InferiorBoundary = inferiorBoundary;
        SuperiorBoundary = superiorBoundary;
    }

    public bool IsWithinBoundaries(T value){
        return InferiorBoundary.CompareTo(value) > 0 && SuperiorBoundary.CompareTo(value) < 0;
    }
}

次に、次のように使用します。

Range<int> myRange = new Range<int>(1,999);
bool isWithinRange = myRange.IsWithinBoundaries(3);

そうすれば、別のタイプに再利用できます。


あなたのRangeオブジェクトが使用する必要がCompareToない、アイテムを比較するための方法を<オペレータ。
2013

あなたは正しいですが、IComparableを実装する場合は、演算子もオーバーライドする必要があります(少なくとも、これは私のVSコード分析が言っていることです)、つまり<は機能します。私は間違っているかもしれませんが、私は多くの経験がないので、これが私の最初の回答です
IEatBagels 2013

いいえ、コンパイラーこれが機能するとは言いません。これはコンパイルされません。オブジェクトを実装IComparableし、<オペレーターをオーバーロードしないことは完全に合理的です。
2013

1

「数値」が範囲内にあるかどうかを確認するとき、あなたはあなたが何を意味するのかを明確にする必要があります、そして2つの数値が等しいとはどういう意味ですか?一般に、すべての浮動小数点数を「イプシロンボール」と呼ばれるものにラップする必要があります。これは、いくつかの小さな値を選択し、2つの値がこれに近いかどうかを示すことで行われます。

    private double _epsilon = 10E-9;
    /// <summary>
    /// Checks if the distance between two doubles is within an epsilon.
    /// In general this should be used for determining equality between doubles.
    /// </summary>
    /// <param name="x0">The orgin of intrest</param>
    /// <param name="x"> The point of intrest</param>
    /// <param name="epsilon">The minimum distance between the points</param>
    /// <returns>Returns true iff x  in (x0-epsilon, x0+epsilon)</returns>
    public static bool IsInNeghborhood(double x0, double x, double epsilon) => Abs(x0 - x) < epsilon;

    public static bool AreEqual(double v0, double v1) => IsInNeghborhood(v0, v1, _epsilon);

これらの2つのヘルパーを配置し、必要な精度なしに任意の数をdoubleとしてキャストできると仮定します。今必要なのは列挙型と別のメソッドだけです

    public enum BoundType
    {
        Open,
        Closed,
        OpenClosed,
        ClosedOpen
    }

他の方法は次のとおりです。

    public static bool InRange(double value, double upperBound, double lowerBound, BoundType bound = BoundType.Open)
    {
        bool inside = value < upperBound && value > lowerBound;
        switch (bound)
        {
            case BoundType.Open:
                return inside;
            case BoundType.Closed:
                return inside || AreEqual(value, upperBound) || AreEqual(value, lowerBound); 
            case BoundType.OpenClosed:
                return inside || AreEqual(value, upperBound);
            case BoundType.ClosedOpen:
                return inside || AreEqual(value, lowerBound);
            default:
                throw new System.NotImplementedException("You forgot to do something");
        }
    }

これはあなたが望むものよりはるかに多いかもしれませんが、それはあなたがいつも丸めに対処し、値が丸められたかどうか、どこに丸められたかを覚えようとすることを防ぎます。必要に応じて、これを簡単に拡張して、任意のイプシロンで動作し、イプシロンを変更できるようにすることができます。


1
static class ExtensionMethods
{
    internal static bool IsBetween(this double number,double bound1, double bound2)
    {
        return Math.Min(bound1, bound2) <= number && number <= Math.Max(bound2, bound1);
    }

    internal static bool IsBetween(this int number, double bound1, double bound2)
    {
        return Math.Min(bound1, bound2) <= number && number <= Math.Max(bound2, bound1);
    }
}

使用法

double numberToBeChecked = 7;

var result = numberToBeChecked.IsBetween(100,122);

var result = 5.IsBetween(100,120);

var result = 8.0.IsBetween(1.2,9.6);


1

受け入れられた回答に関する@Daapのコメントに懸念があり、値を一度しか渡せない場合は、次のいずれかを試すことができます

bool TestRangeDistance (int numberToCheck, int bottom, int distance)
{
  return (numberToCheck >= bottom && numberToCheck <= bottom+distance);
}

//var t = TestRangeDistance(10, somelist.Count()-5, 10);

または

bool TestRangeMargin (int numberToCheck, int target, int margin)
{
  return (numberToCheck >= target-margin && numberToCheck <= target+margin);
}

//var t = TestRangeMargin(10, somelist.Count(), 5);

1

エレガンスに関して、数学的表記に最も近いもの(a <= x <= b)は、読みやすさをわずかに向上させます。

public static bool IsBetween(this int value, int min, int max)
{
    return min <= value && value <= max;
}

詳細な説明:

public static bool IsOutside(this int value, int min, int max)
{
    return value < min || max < value;
}

0

私は境界が切り替わる可能性がある場所でそれを行うためのエレガントな方法を探していました(つまり、値の順序がわからない)。

これは、?:が存在する新しいバージョンのC#でのみ機能します

bool ValueWithinBounds(float val, float bounds1, float bounds2)
{
    return bounds1 >= bounds2 ?
      val <= bounds1 && val >= bounds2 : 
      val <= bounds2 && val >= bounds1;
}

もちろん、目的のために=記号を変更することもできます。型キャストも気に入るかもしれません。範囲内(または等しい)の浮動小数点数の戻り値が必要でした


0

2つの境界値のどちらが最初に大きいかを判別する必要がないため、エレガントです。また、ブランチも含まれていません。

public static bool InRange(float val, float a, float b)
{
    // Determine if val lies between a and b without first asking which is larger (a or b)
    return ( a <= val & val < b ) | ( b <= val & val < a );
}

&+ | ビット演算子
nelsontruran

0

わかりませんが、この方法を使用します。

    public static Boolean isInRange(this Decimal dec, Decimal min, Decimal max, bool includesMin = true, bool includesMax = true ) {

    return (includesMin ? (dec >= min) : (dec > min)) && (includesMax ? (dec <= max) : (dec < max));
}

そして、これは私がそれを使うことができる方法です:

    [TestMethod]
    public void IsIntoTheRange()
    {
        decimal dec = 54;

        Boolean result = false;

        result = dec.isInRange(50, 60); //result = True
        Assert.IsTrue(result);

        result = dec.isInRange(55, 60); //result = False
        Assert.IsFalse(result);

        result = dec.isInRange(54, 60); //result = True
        Assert.IsTrue(result);

        result = dec.isInRange(54, 60, false); //result = False
        Assert.IsFalse(result);

        result = dec.isInRange(32, 54, false, false);//result = False
        Assert.IsFalse(result);

        result = dec.isInRange(32, 54, false);//result = True
        Assert.IsTrue(result);
    }

コードブロックの下に使用例を入力してください。これにより、OPが目的に合っているかどうかをOPが知るのに役立ちます
Gabriel BalsaCantúOct

0

これらは助けることができるいくつかの拡張メソッドです

  public static bool IsInRange<T>(this T value, T min, T max)
where T : System.IComparable<T>
    {
        return value.IsGreaterThenOrEqualTo(min) && value.IsLessThenOrEqualTo(max);
    }


    public static bool IsLessThenOrEqualTo<T>(this T value, T other)
         where T : System.IComparable<T>
    {
        var result = value.CompareTo(other);
        return result == -1 || result == 0;
    }


    public static bool IsGreaterThenOrEqualTo<T>(this T value, T other)
         where T : System.IComparable<T>
    {
        var result = value.CompareTo(other);
        return result == 1 || result == 0;
    }

0

メソッドのパラメーターを検証する場合、どのソリューションもArgumentOutOfRangeExceptionをスローせず、包括的/排他的最小/最大値の簡単/適切な設定を可能にします。

このように使う

public void Start(int pos)
{
    pos.CheckRange(nameof(pos), min: 0);

    if (pos.IsInRange(max: 100, maxInclusive: false))
    {
        // ...
    }
}

私はこれらの美しい関数を書いただけです。また、有効な値に分岐がない(ifが1つ)という利点もあります。最も難しいのは、適切な例外メッセージを作成することです。

/// <summary>
/// Returns whether specified value is in valid range.
/// </summary>
/// <typeparam name="T">The type of data to validate.</typeparam>
/// <param name="value">The value to validate.</param>
/// <param name="min">The minimum valid value.</param>
/// <param name="minInclusive">Whether the minimum value is valid.</param>
/// <param name="max">The maximum valid value.</param>
/// <param name="maxInclusive">Whether the maximum value is valid.</param>
/// <returns>Whether the value is within range.</returns>
public static bool IsInRange<T>(this T value, T? min = null, bool minInclusive = true, T? max = null, bool maxInclusive = true)
    where T : struct, IComparable<T>
{
    var minValid = min == null || (minInclusive && value.CompareTo(min.Value) >= 0) || (!minInclusive && value.CompareTo(min.Value) > 0);
    var maxValid = max == null || (maxInclusive && value.CompareTo(max.Value) <= 0) || (!maxInclusive && value.CompareTo(max.Value) < 0);
    return minValid && maxValid;
}

/// <summary>
/// Validates whether specified value is in valid range, and throws an exception if out of range.
/// </summary>
/// <typeparam name="T">The type of data to validate.</typeparam>
/// <param name="value">The value to validate.</param>
/// <param name="name">The name of the parameter.</param>
/// <param name="min">The minimum valid value.</param>
/// <param name="minInclusive">Whether the minimum value is valid.</param>
/// <param name="max">The maximum valid value.</param>
/// <param name="maxInclusive">Whether the maximum value is valid.</param>
/// <returns>The value if valid.</returns>
public static T CheckRange<T>(this T value, string name, T? min = null, bool minInclusive = true, T? max = null, bool maxInclusive = true)
where T : struct, IComparable<T>
{
    if (!value.IsInRange(min, minInclusive, max, maxInclusive))
    {
        if (min.HasValue && minInclusive && max.HasValue && maxInclusive)
        {
            var message = "{0} must be between {1} and {2}.";
            throw new ArgumentOutOfRangeException(name, value, message.FormatInvariant(name, min, max));
        }
        else
        {
            var messageMin = min.HasValue ? GetOpText(true, minInclusive).FormatInvariant(min) : null;
            var messageMax = max.HasValue ? GetOpText(false, maxInclusive).FormatInvariant(max) : null;
            var message = (messageMin != null && messageMax != null) ?
                "{0} must be {1} and {2}." :
                "{0} must be {1}.";
            throw new ArgumentOutOfRangeException(name, value, message.FormatInvariant(name, messageMin ?? messageMax, messageMax));
        }
    }
    return value;
}

private static string GetOpText(bool greaterThan, bool inclusive)
{
    return (greaterThan && inclusive) ? "greater than or equal to {0}" :
        greaterThan ? "greater than {0}" :
        inclusive ? "less than or equal to {0}" :
        "less than {0}";
}

public static string FormatInvariant(this string format, params object?[] args) => string.Format(CultureInfo.InvariantCulture, format, args);

-2

お探しin [1..100]ですか?それはパスカルだけです。


2
真実ではない、それはパスカルだけではない。多くの現代の言語には、このような機能があります。たとえば、Kotlinでは「パターンマッチング」と呼ばれます。例 when (number) { in 0..9 -> println("1 digit") in 10..99 -> println("2 digits") in 100..999 -> println("3 digits") }
this.myself
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.