4

アプリケーションで特定の数を有効な範囲に制限する必要があります。これを処理するためのデリゲートを作成しました。これが正しい方法かどうかはわかりません。何かがおかしいと感じています。

public delegate int RestrictInteger(int minimum, int maximum, int value);
public delegate decimal RestrictDecimal(decimal minumum, decimal maximum, decimal value);

class GameMath
{
    public static int RestrictNumber(int minimum, int maximum, int value)
    {
        if (value < minimum) { value = minimum; }
        else if (value > maximum) { value = maximum; }
        return value;
    }
    public static decimal RestrictNumber(decimal minimum, decimal maximum, decimal value)
    {
        if (value < minimum) { value = minimum; }
        else if (value > maximum) { value = maximum; }
        return value;
    }
}
public class SomeClass
{
    public int aValue { get; set; }

    public void SetValue(int value)
    {
        RestrictInteger doRestrict = new RestrictInteger(GameMath.RestrictNumber);
        this.aValue = doRestrict(0, 100, value);

    }
}

一方では、署名をさらに追加した場合、それらを使用してさまざまなこと(たとえば、変換、丸めなど)を実行できるようにしたいと考えています。一方、これら2つの署名の間では、コードはまったく同じです。それは大丈夫ですか、それとも他のケースが異なる操作を使用する可能性がある場合でも、これらの両方のケースに適用される1つの操作を作成する方法はありますか?

4

3 に答える 3

21

はい、ジェネリックスでこれを行うことができます-とではありません<>IComparable<T>これらのタイプが自分自身のために実装するという事実を使用する必要があります。

public static T RestrictNumber<T>(T min, T max, T value) where T : IComparable<T>
{
    return value.CompareTo(min) < 0 ? min
         : value.CompareTo(max) > 0 ? max
         : value;
}

(ここでも元のコードを使用できます。ただし、この種の条件演算子の使用が好きです。これにより、機能的な傾向が高まります。)

于 2010-01-14T21:40:37.573 に答える
1

(私はパーティーに遅れていますが、それを撃ちたかったです)

この構文はよく読めると思います。

Restrict.Value(x).ToBetween(0, 100)

これを行うには、制限インターフェイスを定義します。

public interface IRestrictable<T> where T : IComparable<T>
{
    T ToBetween(T minimum, T maximum);
}

次に、実装を提供する静的クラスと、タイプを推測するメソッドを定義します。

public static class Restrict
{
    public static IRestrictable<T> Value<T>(T value) where T : IComparable<T>
    {
        return new Restricter<T>(value);
    }

    private sealed class Restricter<T> : IRestrictable<T> where T : IComparable<T>
    {
        private readonly T _value;

        internal Restricter(T value)
        {
            _value = value;
        }

        public T ToBetween(T minimum, T maximum)
        {
            // Yoink from Jon Skeet

            return _value.CompareTo(minimum) < 0
                ? minimum
                : _value.CompareTo(maximum) > 0 ? maximum : value;
        }
    }
}
于 2010-01-15T00:33:49.980 に答える
1

これらの数値の使用方法によっては、暗黙的な演算子を使用した型が役立つ場合があります。

これにより、<<= >> = +-などの一般的な比較演算子と単項演算子を使用したり、TタイプとRestrictedNumberタイプの使用法を混在させたりできるため、たとえば、RestrictedNumberを期待する任意のメソッドに渡すことができます。範囲外であった可能性のある初期値を保持している間、double。

制限やキャストを実行するためにメソッドを呼び出す必要はありません。宣言時にすべてを設定できます。

使用例と注意事項については、以下の2番目のクラスを参照してください。

オッカムの剃刀を置き忘れた:

public class RestrictedNumber<T> : IEquatable<RestrictedNumber<T>>, IComparable<RestrictedNumber<T>>
    where T: IEquatable<T>,IComparable<T>
{
    T min;
    T max;
    readonly T value;

    public RestrictedNumber(T min, T max, T value)
    {
        this.min = min;
        this.max = max;
        this.value = value;
    }

    public T UnrestrictedValue
    {
        get{ return value; }
    }

    public static implicit operator T(RestrictedNumber<T> n)
    {
        return get_restricted_value(n);
    }

    public static implicit operator RestrictedNumber<T>(T value)
    {
        return new RestrictedNumber<T>(value, value, value);
    }

    static T get_restricted_value(RestrictedNumber<T> n)
    {
        // another yoink from Jon Skeet
        return n.value.CompareTo(n.min) < 0 ? n.min
            : n.value.CompareTo(n.max) > 0 ? n.max
                : n.value;
    }

    T restricted_value
    {
        get { return get_restricted_value(value); }
    }

    public T Min // optional to expose this
    {
        get { return this.min; }
        set { this.min = value; } // optional to provide a setter
    }

    public T Max // optional to expose this
    {
        get { return this.max; }
        set { this.max = value; } // optional to provide a setter
    }

    public bool Equals(RestrictedNumber<T> other)
    {
        return restricted_value.Equals(other);
    }

    public int CompareTo(RestrictedNumber<T> other)
    {
        return restricted_value.CompareTo(other);
    }

    public override string ToString()
    {
        return restricted_value.ToString();
    }

}

public class RestrictedNumberExercise
{
    public void ad_hoc_paces()
    {
        // declare with min, max, and value
        var i = new RestrictedNumber<int>(1, 10, 15);

        Debug.Assert(i == 10d);
        Debug.Assert(i.UnrestrictedValue == 15d);

        // declare implicitly
        // my implementation initially sets min and max equal to value
        RestrictedNumber<double> d = 15d;
        d.Min = 1;
        d.Max = 10;

        Debug.Assert(i == 10d); // compare with other, "true" doubles
        Debug.Assert(i.UnrestrictedValue == 15d); // still holds the original value

        RestrictedNumber<decimal> m = new RestrictedNumber<decimal>(55.5m,55.5m,55.499m);

        Debug.Assert(m == 55.5m);
        Debug.Assert(m > m.UnrestrictedValue); // test out some other operators
        Debug.Assert(m >= m.UnrestrictedValue); // we didn't have to define these
        Debug.Assert(m + 10 == 65.5m); // you even get unary operators

        RestrictedNumber<decimal> other = 50m;

        Debug.Assert(m > other); // compare two of these objects
        Debug.Assert(other <= m); // ...again without having to define the operators
        Debug.Assert(m - 5.5m == other); // unary works with other Ts
        Debug.Assert(m + other == 105.5m); // ...and with other RestrictedNumbers
        Debug.Assert(55.5m - m == 0);
        Debug.Assert(m - m == 0);

        // passing to method that expects the primitive type
        Func<float,float> square_float = f => f * f;
        RestrictedNumber<float> restricted_float = 3;
        Debug.Assert(square_float(restricted_float) == 9f);

        // this sort of implementation is not without pitfalls
        // there are other IEquatable<T> & IComaparable<T> types out there...
        var restricted_string = new RestrictedNumber<string>("Abigail", "Xander", "Yolanda");
        Debug.Assert(restricted_string == "Xander"); // this works
        //Debug.Assert(restricted_string >= "Thomas"); // many operators not supported here

        var pitfall = new RestrictedNumber<int>(1, 100, 200);
        Debug.Assert(pitfall == 100);

        pitfall = 200;
        // Debug.Assert(pitfall == 100);
        // FAIL -- using the implicit operator is effectively
        // a factory method that returns a NEW RestrictedNumber
        // with min and max initially equal to value (in my implementation)
        Debug.Assert(pitfall == 200);

        pitfall = 10;
        Debug.Assert(pitfall.Min == 10 && pitfall.Max == 10);
        pitfall++;
        Debug.Assert(pitfall == 11); // d'oh!
        Debug.Assert(pitfall.Min == 11 && pitfall.Max == 11); // "it goes up to eleven"

        // if you need to change the input value for an existing
        // RestrictedNumber, you could expose a SetValue method
        // and make value not readonly

    }
}

このアプローチをブライアンの流暢なインターフェースと組み合わせて、これをかなり遠くまで進めることができます(ただし、実際にはそうする必要はなく、これはすべてクレイジーなやり過ぎです)。

var n = Restrict<int>._(25).to_be.greater_than(50);
var p = Restrict<double>._(1234.567).to_be.greater_than(0d).and.less_than(50000d)

于 2010-01-15T05:20:26.320 に答える