属性的MinValue和MaxValue属性

20

是否可以创建一个属性来限制数字的最小或最大值。

示例:

[MinValue(1), MaxValue(50)]
public int Size { get; set; }

当我执行Size = -3;时,Size的值必须为1。

我在谷歌上搜索了一下,找不到关于这种行为的单一示例,也许是因为这是不可能实现的?

我将在属性网格中使用这些属性,因此自动验证可能很方便。

目前我用以下方式解决了限制最小值的问题:

    private int size;

    [DefaultValue(8)]
    public int Size
    {
        get
        {
            return size;
        }
        set
        {
            size = Math.Max(value, 1);
        }
    }

所以这就像是MinValue(1)的作用


1
PropertyGrid类不能帮助您实现此功能,它不知道有关您的自定义属性的任何信息。编写自定义TypeConverter是很痛苦的。在属性setter中强制执行范围是简单的方法。 - Hans Passant
5个回答

6

虽然可以创建自定义属性,但属性只是用于注释其成员的元数据,不能改变其行为。

因此,使用普通属性无法获得所需的行为。您需要处理属性以实施所需的行为。

可以考虑使用TypeConverters


4
你可以通过使用PostSharp来优雅地解决此问题,只需编写简单的方面即可。免费版本已足够用于此目的。
[Serializable]
class RangeAttribute : LocationInterceptionAspect 
{
    private int min;
    private int max;

    public RangeAttribute(int min, int max)
    {
        this.min = min;
        this.max = max;
    }

    public override void OnSetValue(LocationInterceptionArgs args)
    {
        int value = (int)args.Value;
        if (value < min) value = min;
        if (value > max) value = max;            
        args.SetNewValue(value);
    }
}

然后就像您想要的那样:

class SomeClass
{
    [Range(1, 50)]
    public int Size { get; set; }
}

一般使用情况下:

var c = new SomeClass();
c.Size = -3;
Console.Output(c.Size);

将输出1。


不改变值,如何通知调用者?我可以直接抛出异常吗? - Brent81

3
是的,这是可能的。请阅读有关自定义属性的内容,网址为MSDN
顺便说一下,已经有一个解决方案可以使用了。它是RangeAttribute,可以让您指定数据字段值的数字范围约束。在MSDN上了解更多信息。

1
我已经检查了MSDN,关于使用属性修改设置值的内容没有任何信息。我还检查了RangeAttribute,它是用于asp.net数据控件的,当我在属性网格中使用它时不会有任何效果。 - Jaex
@Jaex,你在哪里看到它只适用于ASP?? - Noctis
@Noctis 在这里:http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations(v=vs.110).aspx "System.ComponentModel.DataAnnotations 命名空间提供了用于为 ASP.NET MVC 和 ASP.NET 数据控件定义元数据的属性类。" - Jaex
@Noctis 为什么我要尝试在 MSDN 上寻找最佳的解释和示例,当它们已经如此出色了呢?这显然不仅仅是一个 LINQ 的答案,而 MSDN 就像一本百科全书。 - Ondrej Janacek
@Noctis 他还附上了RangeAttribute MSDN链接,它位于DataAnnotations命名空间中。当你问它是否只适用于asp时,我也向你提供了链接。 - Jaex
显示剩余5条评论

2
创建一个扩展
public static class Extensions
{
  public static int FixedValue(this int value, int min, int max)
  {
    if (value >= min && value <= max)
      return value;
    else if (value > max)
      return max;
    else if (value < min)
      return min;
    else return 1;
  }
}

然后:

private int size;
public int Size { get { return size.FixedValue(1, 50); }
                  set { size = value.FixedValue(1, 50); } }

说实话...考虑到给无效属性值添加错误消息的“复杂性”,我觉得@Salvador的答案只有1个赞有点尴尬。 我要点个赞。
  1. 我终于理解了扩展。
  2. 他找到了一种类似但比我自己更好的方法来做这件事。 所以,是的。点个赞。
- user12761381

1
是的,使用自定义属性是可能的(已经指出),但要注意,您将失去自动属性的便利性 - 因为您需要在某个地方应用或检查属性限制,在这种情况下,适当的地方是属性的getter,因此问题的有趣部分是属性的应用。您可以阅读如何访问自定义属性 在此MSDN文章中
MaxValue自定义属性的一个可能解决方案如下:
// simple class for the example
public class DataHolder
{
    private int a;

    [MaxValue(10)]
    public int A 
    { 
        get
        {
            var memberInfo = this.GetType().GetMember("A");
            if (memberInfo.Length > 0)
            {
                // name of property could be retrieved via reflection
                var mia = this.GetType().GetMember("A")[0];
                var attributes = System.Attribute.GetCustomAttributes(mia);
                if (attributes.Length > 0)
                {
                    // TODO: iterate over all attributes and check attribute name
                    var maxValueAttribute = (MaxValue)attributes[0];
                    if (a > maxValueAttribute.Max) { a = maxValueAttribute.Max; }
                }
            }
            return a;
        }
        set
        {
            a = value;
        }
    }
}


// max attribute
public class MaxValue : Attribute
{
    public int Max;

    public MaxValue(int max)
    {
        Max = max;  
    }
}

样例用法:

var data = new DataHolder();
data.A = 12;
Console.WriteLine(data.A);

创建输出:
10

MinValue的代码看起来与MaxValue的代码相同,但if条件将是“小于”而不是“大于”。

感谢您的努力,但不幸的是,在get属性中使用size = Math.Max(value, 1);将是更优雅的解决方案。 - Jaex
这是正确的,但属性的问题在于它们在某个时候和某个地方需要被评估和应用,否则如果它们没有被使用,那么它们有什么用处呢? - keenthinker
也许通过继承PropertyGrid,可以在设置值时评估属性。但是弄清楚这一点将需要太多的工作,因此我将继续使用Math.Max解决方案。 - Jaex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接