2012-11-06 131 views
1

我想在类中进行一些验证,所以我想我可以使用属性。 像这样:获得属性修饰属性的值

public class someClass 
{ 
    [Lenght(200)] 
    public string someStr { get; set; } 
} 
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] 
sealed class Lenght : Attribute 
{ 
    public Lenght(int Lenght) 
    { 
     //some code 
    } 
} 

但我知道,我不能这样做,因为这不是属性如何工作。而且,如果有办法,它会使用我想避免的某种沉重的反射和杂物。

我想要做的验证是这样的:

public class someClass 
{ 

    public string someStr 
    { 
     get 
     { 
      return _someStr; 
     } 
     set 
     { 
      if (value.Length > 200) 
      { 
       throw new Exception("Max Length is 200!"); 
      } 
      else _someStr = value; 
     } 
    } 
    private string _someStr { get; set; } 
} 

但我想做得更快,没有所有这些代码。我想和使用属性一样快。

有一种方法可以做到这一点?

回答

1

通常你不会做这样的东西用属性,但它是可能的,尽管不可取。使用您自己的风险:)(你会放开地狱,如果物业不与LengthAttribute :)饰)

public interface AttributeValidator 
    { 
     void CheckOk(object value); 
    } 

    public class LenghtAttribute : Attribute, AttributeValidator 
    { 
     public int MaxLength { get; private set; } 
     public LenghtAttribute(int lenght) 
     { 
      this.MaxLength = lenght; 
     } 

     public void CheckOk(object value) 
     { 
      var str = value as string; 
      if (str != value) 
       throw new Exception("Not a string!"); 

      if (str != null && str.Length > MaxLength) 
       throw new Exception("To long!"); 
     } 
    } 

    public class DoesNotContain : Attribute, AttributeValidator 
    { 
     public string Chars { get; private set; } 
     public DoesNotContain(string chars) 
     { 
      this.Chars = chars; 
     } 

     public void CheckOk(object value) 
     { 
      var str = value as string; 
      if (str != value) 
       throw new Exception("Not a string!"); 

      if (str != null && Chars.Any(c => str.Contains(c))) 
       throw new Exception("Contains forbidden character!"); 
     } 
    } 

    public class SomeClass 
    { 
     private string _someString; 

     [Lenght(200)] 
     [DoesNotContain("$#2")] 
     public string SomeString 
     { 
      get { return _someString; } 
      set 
      { 
       Utils.Validate("SomeString", this, value); 
       _someString = value; 
      } 
     } 
    } 

    public static class Utils 
    { 
     public static void Validate(string property, object instance, string value) 
     { 
      var validators = instance.GetType().GetProperty(property) 
       .GetCustomAttributes(false).OfType<AttributeValidator>(); 

      foreach (var validator in validators) 
       validator.CheckOk(value); 
     } 
    } 

编辑我已经扩展的例子。仍然是一个可怕的解决方案,但它的工作。

+0

我发布这个问题已经有2年了,今天我在这里回顾我的问题。 从那时起我已经学会了一些C#,现在我不会问这个问题,我也不会使用属性来验证它。 我接受了你的答案,因为你付出了一些努力,我只想指出你在SomeClass中的代码几乎和我在我的问题中发布的一样,但是无论如何。 另外,在对房产验证有一些经验后,我再也不想这样做了,我从不期望在属性中抛出异常。自那以后,我彻底改变了主意。 –

1

属性的目的是向其他组件,工具,编译器等声明关于程序集,类型,成员,参数等的元数据。当然,你可以做你正在问的东西,但这会涉及到反思自己这是一个很长的路要走,只是像你在第二个例子中那样坚持价值。

您应该考虑的是一个验证框架,它为您提供了所有这些内置功能,并允许您单独验证和外部验证。

有许多验证框架可供选择。我头上两个受欢迎的人是FluentValidation和企业图书馆的Validation Application Block

另一方面,也许你只是在寻找一个好的Guard类或Code Contracts。这些服务的用途与验证不同(参见Design by Contract)。你可以想象地将属性(我建议重用Data Annotations)和一些面向方面的编程结合起来,以声明方式为你完成守卫任务(见PostSharp)。但是,这可能只是炫耀:)。

+0

这个FluentValidation框架看起来很有趣,但我不喜欢这个基本验证使用框架的想法......但它似乎很有趣... –

1

你可以创建这需要的字段作为ref参数的方法:

public static void SetWithMaxLength(ref string field, string value, int maxLength) 
{ 
    if(value.Length > maxLength) throw new Exception("Max length is " + maxLength); 
    field = value; 
} 

,那么你可以写你的setter方法为:

set 
{ 
    SetWithMaxLength(ref this._someStr, value, 200); 
} 
+0

是的,我可以学习更多关于参考和出参数...谢谢! –