代码之家  ›  专栏  ›  技术社区  ›  user164226

需要帮助避免跨多个方法签名的代码重复吗

  •  4
  • user164226  · 技术社区  · 16 年前

    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);
    
        }
    }
    

    3 回复  |  直到 16 年前
        1
  •  21
  •   Jon Skeet    16 年前

    是的,您可以使用泛型实现这一点,但不能使用泛型 < 和 > . 您应该使用这些类型实现的事实 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;
    }
    

    (你 能够 仍然在这里使用您的原始代码-尽管我很喜欢这种条件运算符的使用;它满足了我日益增长的功能性倾向。)

        2
  •  1
  •   Bryan Watts    16 年前

    (我参加晚会迟到了,但我想试一试)

    我认为这种语法读起来很好:

    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;
            }
        }
    }
    
        3
  •  1
  •   Jay    16 年前

    根据您将如何使用这些数字,可能会有带有隐式运算符的类型有用的实例。

    它允许您使用常用的比较运算符和一元运算符,例如<<=&燃气轮机>=+-,,为了在T类型和RestrictedNumber类型之间混合使用,例如,您可以将RestrictedNumber传递给任何需要双精度的方法,同时仍然保留可能超出范围的初始值。

    有关用法示例和注释,请参见下面的第二个类。

    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
    
        }
    }
    

    您可以将这种方法与Bryan的流畅界面结合起来,并将其发挥到极致(尽管您可能真的不需要这样做,这完全是疯狂的过度使用)。

    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)