2011-02-23 49 views
2

我有幾個類來實現某個接口。如何驗證C#界面字段中的數據?

在接口而不是實現級別的級別上,是否有任何方式來定義數據驗證規則?

如果不是,那麼建議的模式將從特定類中提取數據驗證規則是什麼? (編輯:在我的情況,我想盡量避免使用一個抽象基類來實現驗證。)

謝謝

回答

8

我會將驗證邏輯分隔到另一個類中。例如,如果你的界面是IFoo,你將有一個FooValidatorValidate(IFoo foo)方法。這將IFoo的實現與有關驗證的業務規則分開。分離是指:

  • 可以使用相同的驗證器類的驗證邏輯不依賴於具體實現的IFoo
  • 您可以使用在不同環境下不同的驗證的IFoo
  • 所有實現,所以管理員可能比客戶有更少的驗證規則,或者您可以嘲笑自動化測試的驗證

本示例實施使用摘要ValidatorBase你最初不需要使用的類,我過早地優化它: - $。

interface IFoo 
{ 
    int Age { get; } 
    string Name { get; } 
} 
class Foo : IFoo 
{ 
    public int Age { get; set; } 
    public string Name { get; set; } 
} 

abstract class ValidatorBase<T> 
{ 
    public class Rule  
    { 
     public Func<T, bool> Test { get; set; } 
     public string Message { get; set; } 
    } 

    protected abstract IEnumerable<Rule> Rules { get; } 

    public IEnumerable<string> Validate(T t) 
    { 
     return this.Rules.Where(r => !r.Test(t)).Select(r => r.Message); 
    } 
} 

class FooValidator : ValidatorBase<IFoo> 
{ 
    protected override IEnumerable<ValidatorBase<IFoo>.Rule> Rules 
    { 
     get 
     { 
      return new Rule[] { 
       new Rule { Test = new Func<IFoo,bool>(foo => foo.Age >= 0), Message = "Age must be greater than zero" }, 
       new Rule { Test = new Func<IFoo,bool>(foo => !string.IsNullOrEmpty(foo.Name)), Message = "Name must be provided" } 
      };      
     } 
    } 
} 

static void Main(string[] args) 
{ 
    var foos = new[] { 
     new Foo { Name = "Ben", Age = 30 }, 
     new Foo { Age = -1 }, 
     new Foo { Name = "Dorian Grey", Age = -140 } 
    }; 

    var fooValidator = new FooValidator(); 

    foreach (var foo in foos) 
    { 
     var messages = fooValidator.Validate(foo); 
     if (!messages.Any()) Console.WriteLine("Valid"); 
     else foreach (var message in messages) Console.WriteLine("Invalid: " + message); 
     Console.WriteLine(); 
    } 
} 

程序運行後給出了這樣的結果:

有效

無效:年齡必須大於零
無效:名稱必須提供

無效:年齡必須大於零

+0

這是一個很好的解決方案。有沒有什麼方法可以將它連接起來,以便可以在屬性集上運行'fooValidator.Validate'? – nonot1 2011-02-24 17:36:39

+0

@ nonot1我真的會在UI層(或MVP表現層)運行驗證,所以假設一個Winforms應用程序,如下所示:'void nameTextBox_changed(..){_foo.Name = this.nameTextBox.Text; this.ShowErrors(_fooValidator.Validate(FOO)); }',而不是試圖通過'foo'本身綁定驗證。非常快速地變得非常混亂。 – 2011-02-24 23:54:55

5

也許使用抽象類是「人在中間人」在那裏你可以放置屬性的驗證規則?

0

你不能在接口級別實現任何代碼 - 所以你不能把驗證登錄到接口。

如果您有驗證框架讀取屬性以執行驗證,則可以向接口成員添加屬性。

要共享驗證代碼只是有單獨的方法,需要接口作爲參數,並在那裏進行驗證,如bool Validate(IMyInterface data){... return true;}

2

是否有可能讓這些類都從一個公共基類派生並在那裏實現驗證邏輯?您可以讓公共基類實現接口,並定義派生類將實現的抽象方法來自定義行爲。

0

首先你不能在接口本身有任何實現。

如果你有這樣的要求,最好用抽象類。

0

你沒有提到你是否使用WinForms,WPF等,正如其他人所說,你不能把這段代碼放到實際的接口中。

建議的方法是在基類上實現IDataErrorInfo。

這是有人用MVVM模式在基類實現這個的一個例子:IDataErrorInfo with MVVM

你不必遵循MVVM,但你可以按照他的文章,對WPF實現驗證。

即使如此,它還是有點笨重,因爲您必須在每個屬性中進行工作。你只能通過將這些屬性放入一個通用的基類來解決這個問題。如果你有興趣嘗試類似Castle Windsor和設置AOP,但是我不會推薦將其綁定到部分完成的應用程序中,但有更復雜的方法。

2

另一種可能性是使用屬性。您可以創建自定義屬性來定義驗證規則

public abstract class ValidationAttribute : Attribute 
{ 
    public abstract bool IsValid(object value); 
} 

public class EvenValidation : ValidationAttribute 
{ 
    public override bool IsValid(object value) 
    { 
      if (!(value is int)) 
      return false; 

      return ((int)value) % 2 == 0; 
    } 
} 

public interface IFoo 
{ 
    [EvenValidation] 
    int SomeValue { get; } 
} 

public static class Validator 
{ 
     public static bool IsValid(object component, object proposedValue, string property) 
     { 
      //Use reflection to look for ValidationAttributes on the property 
      //Use the ValidationAttribute to validate the proposed value 
     } 
} 
+0

這似乎是我的項目的合理解決方案。 (比抽象類方法更重要)。但我不確定你的代碼是如何工作的......如何使用Validator類? – nonot1 2011-02-23 17:33:34

+0

@ nonot1 Validator類提供了一個實用程序來確保給定的屬性是有效的。 你可以用它來輕鬆實現IDataError您的具體實施的IFoo,例如 – cordialgerm 2011-02-23 18:42:10

+0

有趣的問題:是否在「繼承」在類中實現方法的接口方法的屬性? – 2011-02-23 18:56:57