亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C#中設計、使用Fluent API

2020-01-24 02:04:07
字體:
來源:轉載
供稿:網友

我們經常使用的一些框架例如:EF,Automaper,NHibernate等都提供了非常優秀的Fluent API, 這樣的API充分利用了VS的智能提示,而且寫出來的代碼非常整潔。我們如何在代碼中也寫出這種Fluent的代碼呢,我這里介紹3總比較常用的模式,在這些模式上稍加改動或者修飾就可以變成實際項目中可以使用的API,當然如果沒有設計API的需求,對我們理解其他框架的代碼也是非常有幫助。

一、最簡單且最實用的設計

這是最常見且最簡單的設計,每個方法內部都返回return this; 這樣整個類的所有方法都可以一連串的寫完。代碼也非常簡單:

使用起來也非常簡單:

public class CircusPerformer {   public List<string> PlayedItem { get; private set; }    public CircusPerformer()   {     PlayedItem=new List<string>();   }   public CircusPerformer StartShow()   {     //make a speech and start to show     return this;   }   public CircusPerformer MonkeysPlay()   {     //monkeys do some show     PlayedItem.Add("MonkeyPlay");     return this;   }   public CircusPerformer ElephantsPlay()   {     //elephants do some show     PlayedItem.Add("ElephantPlay");     return this;   }   public CircusPerformer TogetherPlay()   {     //all of the animals do some show     PlayedItem.Add("TogetherPlay");     return this;   }   public void EndShow()   {     //finish the show   }

調用:

[Test]    public void All_shows_can_be_invoked_by_fluent_way()    {      //Arrange      var circusPerformer = new CircusPerformer();             //Act      circusPerformer        .MonkeysPlay()        .ElephantsPlay()        .StartShow()        .TogetherPlay()        .EndShow();       //Assert      circusPerformer.PlayedItem.Count.Should().Be(3);      circusPerformer.PlayedItem.Contains("MonkeysPlay");      circusPerformer.PlayedItem.Contains("ElephantsPlay");      circusPerformer.PlayedItem.Contains("TogetherPlay");    }

但是這樣的API有個瑕疵,馬戲團circusPerformer在表演時是有順序的,首先要調用StartShow(),其次再進行各種表演,表演結束后要調用EndShow()結束表演,但是顯然這樣的API沒法滿足這樣的需求,使用者可以隨心所欲改變調用順序。

如上圖所示,vs將所有的方法都提示了出來。

我們知道,作為一個優秀的API,要盡量避免讓使用者犯錯,比如要設計private 字段,readonly 字段等都是防止使用者去修改內部數據從而導致出現意外的結果。

二、設計具有調用順序的Fluent API

在之前的例子中,API設計者期望使用者首先調用StartShow()方法來初始化一些數據,然后進行表演,最后使用者方可調用EndShow(),實現的思路是將不同種類的功能抽象到不同的接口中或者抽象類中,方法內部不再使用return this,取而代之的是return INext;

根據這個思路,我們將StartShow(),和EndShow()方法抽象到一個類中,而將馬戲團的表演抽象到一個接口中:

public abstract class Performer {   public abstract ICircusPlayer CircusPlayer { get; }   public abstract ICircusPlayer StartShow();   public abstract void EndShow(); }
public interface ICircusPlayer{  IList PlayedItem { get; }  ICircusPlayer MonkeysPlay();  ICircusPlayer ElephantsPlay();  Performer TogetherPlay();}


有了這樣的分類,我們重新設計API,將StartShow()和EndShow()設計在CircusPerfomer中,將馬戲團的表演項目設計在CircusPlayer中:

public class CircusPerformer:Performer {   private ICircusPlayer _circusPlayer;    override public ICircusPlayer CircusPlayer { get { return _circusPlayer; } }    public override ICircusPlayer StartShow()   {     //make a speech and start to show     _circusPlayer=new CircusPlayer(this);     return _circusPlayer;   }      public override void EndShow()   {     //finish the show   } }

public class CircusPlayer:ICircusPlayer {   private readonly Performer _performer;   public IList PlayedItem { get; private set; }    public CircusPlayer(Performer performer)   {     _performer = performer;     PlayedItem=new List();   }    public ICircusPlayer MonkeysPlay()   {     PlayedItem.Add("MonkeyPlay");     //monkeys do some show     return this;   }    public ICircusPlayer ElephantsPlay()   {     PlayedItem.Add("ElephantPlay");     //elephants do some show     return this;   }    public Performer TogetherPlay()   {     PlayedItem.Add("TogetherPlay");     //all of the animals do some show     return _performer;   } }

這樣的API可以滿足我們的要求,在馬戲團circusPerformer實例上只能調用StartShow()和EndShow()

調用完StartShow()后方可調用各種表演方法。

當然由于我們的API很簡單,所以這個設計還算說得過去,如果業務很復雜,需要考慮眾多的情形或者順序我們可以進一步完善,實現的基本思想是利用裝飾者模式和擴展方法,由于園子里的dax.net在很早前就發表了相關博客在C#中使用裝飾器模式和擴展方法實現Fluent Interface,所以大家可以去看這篇文章的實現方案,該設計應該可以說是終極模式,實現過程也較為復雜。

三、泛型類的Fluent設計

泛型類中有個不算問題的問題,那就是泛型參數是無法省略的,當你在使用var list=new List<string>()這樣的類型時,必須指定準確的類型string。相比而言泛型方法中的類型時可以省略的,編譯器可以根據參數推斷出參數類型,例如

var circusPerfomer = new CircusPerfomerWithGenericMethod();      circusPerfomer.Show<Dog>(new Dog());      circusPerfomer.Show(new Dog());


如果想省略泛型類中的類型有木有辦法?答案是有,一種還算優雅的方式是引入一個非泛型的靜態類,靜態類中實現一個靜態的泛型方法,方法最終返回一個泛型類型。這句話很繞口,我們不妨來看個一個畫圖板實例吧。

定義一個Drawing<TShape>類,此類可以繪出TShape類型的圖案

public class Drawing<TShape> where TShape :IShape  {    public TShape Shape { get; private set; }    public TShape Draw(TShape shape)    {      //drawing this shape      Shape = shape;      return shape;    }  }

定義一個Canvas類,此類可以畫出Pig,根據傳入的基本形狀,調用對應的Drawing<TShape>來組合出一個Pig來

public void DrawPig(Circle head, Rectangle mouth)   {     _history.Clear();     //use generic class, complier can not infer the correct type according to parameters     Register(       new Drawing<Circle>().Draw(head),       new Drawing<Rectangle>().Draw(mouth)       );   }

這段代碼本身是非常好懂的,而且這段代碼也很clean。如果我們在這里想使用一下之前提到過的技巧,實現一個省略泛型類型且比較Fluent的方法我們可以這樣設計:

首先這樣的設計要借助于一個靜態類:

public static class Drawer {   public static Drawing<TShape> For<TShape>(TShape shape) where TShape:IShape   {     return new Drawing<TShape>();   } }

 然后利用這個靜態類畫一個Dog

public void DrawDog(Circle head, Rectangle mouth)   {     _history.Clear();     //fluent implements     Register(       Drawer.For(head).Draw(head),       Drawer.For(mouth).Draw(mouth)     );   }

可以看到這里已經變成了一種Fluent的寫法,寫法同樣比較clean。寫到這里我腦海中浮現出來了一句”費這勁干嘛”,這也是很多人看到這里要想說的,我只能說你完全可以把這當成是一種奇技淫巧,如果哪天遇到使用的框架有這種API,你能明白這是怎么回事就行。

四、案例

寫到這里我其實還想舉一個例子來說說這種技巧在有些情況下是很常用的,大家在寫EF配置,Automaper配置的時候經常這樣寫:

xx.MapPath(        Path.For(_student).Property(x => x.Name),        Path.For(_student).Property(x => x.Email),        Path.For(_customer).Property(x => x.Name),        Path.For(_customer).Property(x => x.Email),        Path.For(_manager).Property(x => x.Name),        Path.For(_manager).Property(x => x.Email)        )


這樣的寫法就是前面的技巧改變而來,我們現在設計一個Validator,假如說這個Validator需要批量對Model的字段進行驗證,我們也需要定義一個配置文件,配置某某Model的某某字段應該怎么樣,利用這個配置我們可以驗證出哪些數據不符合這個配置。

配置文件類Path的關鍵代碼:

public class Path<TModel> {   private TModel _model;   public Path(TModel model)   {     _model = model;   }   public PropertyItem<TValue> Property<TValue>(Expression<Func<TModel, TValue>> propertyExpression)   {     var item = new PropertyItem<TValue>(propertyExpression.PropertyName(), propertyExpression.PropertyValue(_model),_model);     return item;   } }


為了實現fluent,我們還需要定義一個靜態非泛型類,

public static class Path  {    public static Path<TModel> For<TModel>(TModel model)    {      var path = new Path<TModel>(model);      return path;    }  }

定義Validator,這個類可以讀取到配置的信息,

public Validator<TValue> MapPath(params PropertyItem<TValue>[] properties)   {     foreach (var propertyItem in properties)     {       _items.Add(propertyItem);     }     return this;   }

最后調用

[Test]   public void Should_validate_model_values()   {      //Arrange     var validator = new Validator<string>();     validator.MapPath(       Path.For(_student).Property(x => x.Name),       Path.For(_student).Property(x => x.Email),       Path.For(_customer).Property(x => x.Name),       Path.For(_customer).Property(x => x.Email),       Path.For(_manager).Property(x => x.Name),       Path.For(_manager).Property(x => x.Email)       )      .OnCondition((model)=>!string.IsNullOrEmpty(model.ToString()));           //Act     validator.Validate();      //Assert     var result = validator.Result();     result.Count.Should().Be(3);     result.Any(x => x.ModelType == typeof(Student) && x.Name == "Email").Should().Be(true);     result.Any(x => x.ModelType == typeof(Customer) && x.Name == "Name").Should().Be(true);     result.Any(x => x.ModelType == typeof(Manager) && x.Name == "Email").Should().Be(true);   }


這樣的Fluent API語言更加清晰并且不失優雅, Path.For(A).Property(x=>x.Name).OnCondition(B),這句話可以翻譯為,對A的屬性Name設置條件為B。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美性xxxx| 亚洲第一视频网| 91国内产香蕉| 亚洲精品999| 成人黄色影片在线| 亚洲欧美日韩国产精品| 亚洲欧洲一区二区三区在线观看| 欧美午夜激情在线| 亚洲精品理论电影| 91九色综合久久| 亚洲欧美日韩精品久久奇米色影视| 精品久久久久久久久久久久久久| 亚洲品质视频自拍网| 亚洲欧美日韩网| 免费97视频在线精品国自产拍| 亚洲成**性毛茸茸| 色中色综合影院手机版在线观看| 亚洲成人a级网| 久久久久久久一区二区三区| 亚洲国产精品久久91精品| 美女福利精品视频| 91中文字幕在线| 欧美不卡视频一区发布| 日韩精品一二三四区| 精品亚洲一区二区三区四区五区| 最近2019中文字幕大全第二页| 欧美日韩在线视频观看| 久久久免费观看| 中文字幕av一区| 国产九九精品视频| 中文字幕av日韩| 国产丝袜一区视频在线观看| 久久久精品国产网站| 国产精品入口夜色视频大尺度| 久久久视频免费观看| 久热精品视频在线| 成人欧美一区二区三区黑人孕妇| 午夜免费在线观看精品视频| 亚洲一区二区黄| 日韩精品在线电影| 久久久久久久久久久久久久久久久久av| 九九热精品视频| 91在线免费看网站| 精品调教chinesegay| 57pao成人永久免费视频| 久久久久久网站| 亚洲精品wwwww| 亚洲va国产va天堂va久久| 国产精品扒开腿做爽爽爽视频| 国产精品99久久久久久白浆小说| 国产视频精品免费播放| 亚洲理论电影网| 最好看的2019年中文视频| 亚洲激情视频在线播放| 51色欧美片视频在线观看| 亚洲在线观看视频网站| 最近的2019中文字幕免费一页| 欧美色视频日本高清在线观看| 久久久免费观看视频| 久久久99免费视频| 久久免费视频在线观看| 亚洲在线观看视频网站| 欧美精品久久久久久久久| 欧美人与性动交| 久久99视频免费| 亚洲国产精品热久久| 久久香蕉国产线看观看网| 日本一区二区在线免费播放| 成人精品aaaa网站| 亚洲美女免费精品视频在线观看| 九九久久久久久久久激情| 51色欧美片视频在线观看| 91视频国产精品| 日韩中文有码在线视频| 国产亚洲精品久久久久动| 亚洲人成人99网站| 日韩欧美在线播放| 亚洲精品在线观看www| 久久精品99国产精品酒店日本| 亚洲欧美激情另类校园| 亚洲欧美日韩在线一区| 尤物九九久久国产精品的分类| 亚洲va国产va天堂va久久| 亚洲va欧美va国产综合剧情| 日韩国产高清视频在线| 欧美性猛交视频| 最近2019中文字幕第三页视频| 国产一区在线播放| 亚洲欧美日韩天堂| 亚洲综合自拍一区| 国产精品日本精品| 亚洲欧美制服中文字幕| 国产日韩欧美在线观看| 欧美午夜性色大片在线观看| 亚洲欧美中文另类| 亚洲图片欧洲图片av| 欧亚精品在线观看| 国产综合在线看| 久久免费视频在线观看| 91高清视频在线免费观看| 久久久久久噜噜噜久久久精品| 日本一本a高清免费不卡| 69久久夜色精品国产7777| 国产精品美女www爽爽爽视频| 国产成人拍精品视频午夜网站| 色yeye香蕉凹凸一区二区av| 成人精品aaaa网站| 一本一道久久a久久精品逆3p| 日韩av一区二区在线观看| 国产精品久久精品| 亚洲大胆美女视频| 国产在线视频2019最新视频| 一区二区三区四区在线观看视频| 亚洲三级黄色在线观看| 欧美丝袜第一区| 中文在线资源观看视频网站免费不卡| 国产精品va在线播放我和闺蜜| 国产成人综合av| 亚洲人成网站色ww在线| 欧美日韩午夜激情| 亚洲欧美一区二区三区在线| 在线性视频日韩欧美| 久久免费精品日本久久中文字幕| 福利视频导航一区| 久久久999精品视频| 久久久久久久国产| 欧美性受xxx| 国产一区二区久久精品| 国产做受高潮69| 国产一区二区视频在线观看| 国产丝袜一区二区| 久久久精品网站| 亚洲精品久久久久久久久久久久久| 亚洲国产精品va在线| 久久九九热免费视频| 精品国产精品三级精品av网址| 亚洲成年网站在线观看| 国产精品久久久久高潮| 欧美国产日韩一区二区| 欧美激情一区二区久久久| 自拍视频国产精品| 国产日韩欧美91| 欧美激情久久久久| 国产精品18久久久久久首页狼| 清纯唯美日韩制服另类| 国产精品女主播视频| 欧美色videos| 欧美大片在线免费观看| 一区二区三区四区视频| 自拍偷拍亚洲一区| 在线一区二区日韩| 欧美成人午夜剧场免费观看| 欧美黑人巨大精品一区二区| 色偷偷噜噜噜亚洲男人的天堂| 九九久久综合网站| 欧美老少配视频| 欧美黄色免费网站| 欧美视频一区二区三区…| 亚洲电影av在线| 欧美日韩国内自拍| 91九色蝌蚪国产| 热久久视久久精品18亚洲精品| 亚洲欧美国产制服动漫| 亚洲人精品午夜在线观看|