模型驗證組件 FluentValidation

FluentValidation?是 .NET 下的模型驗證組件,和 ASP.NET MVC 基于Attribute 聲明式驗證的不同處,其利用表達式語法鏈式編程,使得驗證組件與實體分開。正如 FluentValidation?的 介紹:

A small validation library for .NET that uses a fluent interface and lambda expressions for building validation rules for your business objects.

使用后,只能用一句話來形容:真乃神器也!

項目地址:http://fluentvalidation.codeplex.com/

想體驗 Lambda Expression 流暢的感覺嗎,下面 let's go!

首先,你需要通過 NuGet 獲取 FluentValidation、FluentValidation.MVC3 包,我當前使用的版本如下:

<?xml version="1.0" encoding="utf-8"?>
<packages><package id="FluentValidation" version="3.3.1.0" /><package id="FluentValidation.MVC3" version="3.3.1.0" />
</packages>

快速入門

1. 建立模型類

為了演示,我這里建了一個 Person 類,并且假設有下面這些 Property(屬性)。

/// <summary>
/// 個人
/// </summary>
public class Person
{/// <summary>/// 姓/// </summary>public string Surname { get; set; }/// <summary>/// 名/// </summary>public string Forename { get; set; }/// <summary>/// 公司/// </summary>public string Company { get; set; }/// <summary>/// 地址/// </summary>public string Address { get; set; }/// <summary>/// 郵政編碼/// </summary>public string Postcode { get; set; }/// <summary>/// 個人空間的地址的別名,比如:bruce-liu-cnblogs、cnblogs_bruce_liu/// </summary>public string UserZoneUrl { get; set; }
}

根據 FluentValidation 的使用方法,我們直接可以在 Person 類上面直接標記對應的 Validator,比如: [Validator(typeof(PersonValidator))]。但如果我們的模型層(Model Layer)不允許修改(假設),并且你像我一樣喜歡干凈的模型層,不想要標記太多業務型的 Attribute 時,我們就使用繼承的方式來標記,在派生類上標記。下面我們建一個 Customer 類,繼承自 Person 類,并且再增加 2 個 Property(屬性),最后標記 Validator Attribute。

[Validator(typeof(CustomerValidator))]
public class Customer : Person
{/// <summary>/// 是否有折扣/// </summary>public bool HasDiscount { get; set; }/// <summary>/// 折扣/// </summary>public float Discount { get; set; }}

2. 建立模型類相應的 FluentValidation 驗證類

public class CustomerValidator : AbstractValidator<Customer>
{public CustomerValidator(){// 在這里寫驗證規則,比如:// Cascade(FluentValidation.CascadeMode.StopOnFirstFailure) 可以指定當前 CustomerValidator 的驗證模式,可重寫全局驗證模式RuleFor(customer => customer.Surname).Cascade(FluentValidation.CascadeMode.StopOnFirstFailure).NotEmpty().Length(3, int.MaxValue).WithLocalizedName(() => "姓").WithLocalizedMessage(() => "親,{PropertyName}不能為空字符串,并且長度大于{0}!!!");// 更多...// 更多...}
}

3. 在 Global.asax 里面的 Application_Start 中配置 FluentValidation

默認情況下,FluentValidation 使用的驗證錯誤消息是英文的,且官方自帶的語言包中沒有中文,于是我自己就手動翻譯,建立了一個資源文件 FluentValidationResource.resx,并且在 Global.asax 中配置。

protected void Application_Start()
{ConfigureFluentValidation();
}protected void ConfigureFluentValidation()
{// 設置 FluentValidation 默認的資源文件提供程序 - 中文資源ValidatorOptions.ResourceProviderType = typeof(FluentValidationResource);/* 比如驗證用戶名 not null、not empty、length(2,int.MaxValue) 時,鏈式驗證時,如果第一個驗證失敗,則停止驗證 */ValidatorOptions.CascadeMode = CascadeMode.StopOnFirstFailure; // ValidatorOptions.CascadeMode 默認值為:CascadeMode.Continue// 配置 FluentValidation 模型驗證為默認的 ASP.NET MVC 模型驗證FluentValidationModelValidatorProvider.Configure();
}

FluentValidationResource 代碼中的 Key-Value 如下(PS:由于不知道怎么貼 Resource 文件中的代碼,我就用截圖了):

翻譯得不好,請多多包涵!從這里下載

4. 客戶端調用

本來用控制臺程序就可以調用的,由于筆者建立的項目是 ASP.NET MVC 項目,本文的重點也是 FluentValidation 在 ASP.NET MVC 中使用,于是就在 Action 里面驗證了。在 HomeController 的 Index 方法里面的代碼如下:

public ActionResult Index()
{/* 下面的例子驗證 FluentValidation 在 .net 中的使用,非特定與 ASP.NET MVC */Customer customer = new Customer(); // 我們這里直接 new 了一個 Customer 類,看看模型驗證能否通過CustomerValidator validator = new CustomerValidator();ValidationResult results = validator.Validate(customer); // 或者拋出異常 validator.ValidateAndThrow(customer);bool validationSucceeded = results.IsValid;IList<ValidationFailure> failures = results.Errors;StringBuilder textAppender = new StringBuilder();if (!results.IsValid){foreach (var failureItem in failures){textAppender.Append("<br/>==========================================<br/>");textAppender.AppendFormat("引起失敗的屬性值為:{0}<br/>", failureItem.AttemptedValue);textAppender.AppendFormat("被關聯的失敗狀態為:{0}<br/>", failureItem.CustomState);textAppender.AppendFormat("錯誤消息為:{0}<br/>", failureItem.ErrorMessage);textAppender.AppendFormat("Property(屬性)為:{0}<br/>", failureItem.PropertyName);textAppender.Append("<br/>==========================================<br/>");}}ViewBag.Message = textAppender.ToString();return View();
}

最后,運行就能看到效果!

進階篇

1. 屬性類(Property Class)的驗證

既然是顧客,那么顧客就可能會有訂單,我們建立一個 Order 類,把 Customer 類作為 Order 類的一個 Property(屬性)。

/// <summary>
/// 訂單
/// </summary>
[Validator(typeof(OrderValidator))]
public class Order
{public Customer Customer { get; set; }/// <summary>/// 價格/// </summary>public decimal Price { get; set; }
}

相應的,我們還需要建立一個驗證類 OrderValidator。為了共用 CustomerValidator 類,我們需要在 OrderValidator 類的構造函數中,為 Order 類的 Customer 屬性指定 Validator。

/// <summary>
/// 訂單驗證類
/// </summary>
public class OrderValidator : AbstractValidator<Order>
{public OrderValidator() {RuleFor(order => order.Price).NotNull().GreaterThanOrEqualTo(0m).WithLocalizedName(() => "價格");// 重用 CustomerValidatorRuleFor(order => order.Customer).SetValidator(new CustomerValidator());}
}

在 ASP.NET MVC 中使用時,在 Action 方法的參數上,可以像使用 Bind Attribute 一樣:

public ActionResult AddCustomer([Bind(Include = "Company", Exclude = "Address")]Customer customer)

使用 CustomizeValidator Attribute,來指定要驗證的 Property(屬性):

[HttpGet]
public ActionResult AddCustomer()
{return View(new Customer());
}[HttpPost]
public ActionResult AddCustomer([CustomizeValidator(Properties="Surname,Forename")] Customer customer)
{/* 在 Action 的參數上標記  CustomizeValidator 可以指定 Interceptor(攔截器)、Properties(要驗證的屬性,以逗號分隔)。如果指定了 Properties (要驗證的屬性,以逗號分隔),請注意是否別的屬性有客戶端驗證,導致客戶端提交不了,而服務器端又可以不用驗證。*/if (!ModelState.IsValid){return View(customer);}return Content("驗證通過");
}

由此可見,FluentValidation 真是用心良苦,這都想到了,不容易啊!

擴展篇

1. 完善 CustomerValidator

接下來,我們繼續 完善 CustomerValidator ,增加更多的驗證規則。

public class CustomerValidator : AbstractValidator<Customer>
{public CustomerValidator(){// CascadeMode = CascadeMode.StopOnFirstFailure; 可以指定當前 CustomerValidator 的驗證模式,可重寫全局驗證模式RuleFor(customer => customer.Surname).Cascade(FluentValidation.CascadeMode.StopOnFirstFailure).NotEmpty().Length(3, int.MaxValue).WithLocalizedName(() => "姓").WithLocalizedMessage(() => "親,{PropertyName}不能為空字符串,并且長度大于{0}!!!");// 注意:調用 Cascade(FluentValidation.CascadeMode.StopOnFirstFailure) 表示當一個驗證條件失敗后,不再繼續驗證RuleFor(customer => customer.Forename).NotEmpty().WithLocalizedName(() => "名").WithLocalizedMessage(() => "{PropertyName} 一定要不為空,Do you know ?");RuleFor(customer => customer.Company).NotNull().WithLocalizedName(() => "公司名稱").WithMessage(string.Format("{{PropertyName}} 不能 \"{0}\",下次記住哦,{1}!", "為空", "呵呵"));RuleFor(customer => customer.Discount).NotEqual(0).WithLocalizedName(() => "折扣").When(customer => customer.HasDiscount);RuleFor(customer => customer.Address).Length(20, 250).WithLocalizedName(() => "地址").Matches("^[a-zA-Z]+$").WithLocalizedMessage(() => "地址的長度必須在 20 到 250 個字符之間,并且只能是英文字符!");RuleFor(customer => customer.Postcode).Must(BeAValidPostcode).WithLocalizedName(() => "郵政編碼").WithMessage("請指定一個合法的郵政編碼");// 注意:如果用了 Must 驗證方法,則沒有客戶端驗證。Custom((customer, validationContext) =>{bool flag1 = customer.HasDiscount;bool flag2 = !validationContext.IsChildContext;return flag1 && flag2 && customer.Discount > 0 ? null : new ValidationFailure("Discount", "折扣錯誤", customer.Discount);});}/// <summary>/// 檢查是否是合法的郵政編碼/// </summary>/// <param name="postcode"></param>/// <returns></returns>private bool BeAValidPostcode(string postcode){if (!string.IsNullOrEmpty(postcode) && postcode.Length == 6){return true;}return false;}
}

當我想要給 Customer.UserZoneUrl(個人空間的地址的別名) 寫驗證規則的時候,我發現它的驗證規則可以提取出來,方便下次有類似的功能需要用到。那能不能像調用 NotNull() 、NoEmpty() 方法那樣,調用我們寫的 EntryName() 呢?答案:當然可以!

這樣調用怎么樣?

RuleFor(customer => customer.UserZoneUrl).EntryName();

其中 EntryName() 是一個擴展方法。

using FluentValidation;public static class FluentValidatorExtensions
{public static IRuleBuilderOptions<T, string> EntryName<T>(this IRuleBuilder<T, string> ruleBuilder){return ruleBuilder.SetValidator(new EntryNameValidator());}
}

我們看到,調用 EntryName 擴展方法其實是調用另外一個 Validator - EntryNameValidator。

public class EntryNameValidator : PropertyValidator, IRegularExpressionValidator
{private readonly Regex regex;const string expression = @"^[a-zA-Z0-9][\w-_]{1,149}$";public EntryNameValidator(): base(() => ExtensionResource.EntryName_Error){regex = new Regex(expression, RegexOptions.IgnoreCase);}protected override bool IsValid(PropertyValidatorContext context){if (context.PropertyValue == null) return true;if (!regex.IsMatch((string)context.PropertyValue)){return false;}return true;}public string Expression{get { return expression; }}
}

這里我們的 EntryNameValidator 除了繼承自 PropertyValidator,還實現了 IRegularExpressionValidator 接口。為什么要實現 IRegularExpressionValidator 接口 呢?是因為可以共享由 FluentValidation 帶來的好處,比如:客戶端驗證等等。

其中 ExtensionResource 是一個資源文件,我用來擴展 FluentValidation 時使用的資源文件。

2. 復雜驗證

下面我們再建立一個 Pet(寵物)類,為 Customer 類增加一個 public List<Pet> Pets { get; set; } 屬性。

/// <summary>
/// 顧客類
/// </summary>
[Validator(typeof(CustomerValidator))]
public class Customer : Person
{/// <summary>/// 是否有折扣/// </summary>public bool HasDiscount { get; set; }/// <summary>/// 折扣/// </summary>public float Discount { get; set; }/// <summary>/// 一個或多個寵物/// </summary>public List<Pet> Pets { get; set; }}/// <summary>
/// 寵物類
/// </summary>
public class Pet
{public string Name { get; set; }
}

那 FluentValidation 對集合的驗證,該如何驗證呢?下面我們要求顧客的寵物不能超過 10 個。你一定想到了用下面的代碼實現:

Custom(customer =>
{return customer.Pets.Count >= 10? new ValidationFailure("Pets", "不能操作 10 個元素"): null;
});

或者我們寫一個自定義的 Property(屬性)驗證器 ListMustContainFewerThanTenItemsValidator<T>,讓它繼承自 PropertyValidator

public class ListMustContainFewerThanTenItemsValidator<T> : PropertyValidator
{public ListMustContainFewerThanTenItemsValidator(): base("屬性 {PropertyName} 不能超過 10 個元素!"){// 注意:這里的錯誤消息也可以用資源文件}protected override bool IsValid(PropertyValidatorContext context){var list = context.PropertyValue as IList<T>;if (list != null && list.Count >= 10){return false;}return true;}
}

應用這個屬性驗證器就很容易了,在 Customer 的構造函數中:

RuleFor(customer => customer.Pets).SetValidator(new ListMustContainFewerThanTenItemsValidator<Pet>());

再或者為了公用,寫一個擴展方法,擴展 IRuleBuilder<T, IList<TElement>> 類

/// <summary>
/// 定義擴展方法,是為了方便調用。
/// </summary>
public static class MyValidatorExtensions
{public static IRuleBuilderOptions<T, IList<TElement>> MustContainFewerThanTenItems<T, TElement>(this IRuleBuilder<T, IList<TElement>> ruleBuilder){return ruleBuilder.SetValidator(new ListMustContainFewerThanTenItemsValidator<TElement>());}
}

調用也像上面調用 EntryName() 一樣,直接調用:

RuleFor(customer => customer.Pets).MustContainFewerThanTenItems();

3. 與 IoC 容器(Autofac、Unity、StructureMap等)集成

下面以 Autofac 為例進行演示

1. 創建自己的 ValidatorFactory

比如我這里創建為 AutofacValidatorFactory,繼承自 FluentValidation.ValidatorFactoryBase,而 ValidatorFactoryBase 本身是實現了 IValidatorFactory 的。IValidatorFactory 的代碼如下:

// 摘要:
//     Gets validators for a particular type.
public interface IValidatorFactory
{// 摘要://     Gets the validator for the specified type.IValidator<T> GetValidator<T>();//// 摘要://     Gets the validator for the specified type.IValidator GetValidator(Type type);
}

ValidatorFactoryBase 的代碼如下:

public abstract class ValidatorFactoryBase : IValidatorFactory
{protected ValidatorFactoryBase();public abstract IValidator CreateInstance(Type validatorType);public IValidator<T> GetValidator<T>();public IValidator GetValidator(Type type);
}

我們看到 ValidatorFactoryBase 其實是把 IValidatorFactory 接口的 2 個方法給實現了,但核心部分還是抽象出來了,那我們的 AutofacValidatorFactory 需要根據 Autofac 的使用方法進行編碼,代碼如下:

public class AutofacValidatorFactory : ValidatorFactoryBase
{private readonly IContainer _container;public AutofacValidatorFactory(IContainer container){_container = container;}/// <summary>/// 嘗試創建實例,返回值為 NULL 表示不應用 FluentValidation 來做 MVC 的模型驗證/// </summary>/// <param name="validatorType"></param>/// <returns></returns>public override IValidator CreateInstance(Type validatorType){object instance;if (_container.TryResolve(validatorType, out instance)){return instance as IValidator;}return null;}
}

2. 在 Application_Start 中注冊 Autofac

protected void Application_Start()
{RegisterAutofac();
}protected void RegisterAutofac()
{// 注冊 IoCContainerBuilder builder = new ContainerBuilder();builder.RegisterNewsManagement();// 創建 containerIContainer _container = builder.Build();// 在 NewsManagement 模型下設置 container_container.SetAsNewsManagementResolver();ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(new AutofacValidatorFactory(_container)));DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
}

其中上面那 2 個方法(RegisterNewsManagement、SetAsNewsManagementResolver)是擴展方法,代碼如下:

public static class AutofacExtensions
{public static void RegisterNewsManagement(this ContainerBuilder builder){builder.RegisterType<NewsCategoryValidator>().As<IValidator<NewsCategoryModel>>();builder.RegisterType<NewsValidator>().As<IValidator<NewsModel>>();builder.RegisterControllers(typeof(MvcApplication).Assembly);}public static void SetAsNewsManagementResolver(this IContainer contaner){DependencyResolver.SetResolver(new AutofacDependencyResolver(contaner));}
}

至此,我們的模型上面就可以注釋掉對應的 Attribute 了。

/// <summary>
/// 文章表模型
/// </summary>//[Validator(typeof(NewsValidator))]
public class NewsModel : NewsEntity
{}


---------------------
作者:惟楚有才
來源:CSDN
原文:https://blog.csdn.net/qq289523052/article/details/23739243
版權聲明:本文為作者原創文章,轉載請附上博文鏈接!
內容解析By:CSDN,CNBLOG博客文章一鍵轉載插件

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284224.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284224.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284224.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

第二屆中國PWA開發者日

點擊藍字關注我們活動介紹為加速推動漸進式 Web 應用 (PWA) 在中國的發展&#xff0c;微軟與英特爾攜手舉辦“第二屆中國 PWA 開發者日”。本次活動邀請一眾業界大咖圍繞 PWA 展開分享&#xff0c;探討最新技術進展&#xff0c;及 PWA 生態的實踐與落地。期待與您線上相聚。活動…

【GlobalMapper精品教程】018:提取影像數據的范圍生成矢量圖層

文章目錄 1. 加載影像數據2. 生成邊界3. 導出矢量范圍4. 背景影響邊界解決辦法1. 加載影像數據 以DSM為例,加載如下所示: 2. 生成邊界 在影像圖層上右鍵→圖層→【邊界框/覆蓋-創建圖層覆蓋框/多邊形區要素】,如下圖所示: 選擇【否】。 邊界創建完成。 3. 導出矢量范圍 …

MPMoviePlayerController屬性方法簡介

屬性說明property (nonatomic, copy) NSURL *contentURL播放媒體URL&#xff0c;這個URL可以是本地路徑&#xff0c;也可以是網絡路徑property (nonatomic, readonly) UIView *view播放器視圖&#xff0c;如果要顯示視頻必須將此視圖添加到控制器視圖中property (nonatomic, re…

在Leangoo里怎么設置看板周期?

設置看板周期有兩種方式&#xff1a; 1&#xff09;點擊看板上的看板周期時間直接修改 2&#xff09;通過菜單 設置看板周期 瀏覽器訪問官網鏈接&#xff1a;www.leangoo.com 轉載于:https://www.cnblogs.com/shineshine/p/5663104.html

consul部署多節點和consul-template部署

一.consul的介紹 1.1consul是什么&#xff1f; Consul是HashiCorp公司推出的開源工具,用于實現分布式系統的服務發現與配置。 Consul是分布式的、高可用的、可橫向擴展的。它具備以下特性 : service discovery:consul通過DNS或者HTTP接口使服務注冊和服務發現變的很容易,一些外…

基于ABP實現DDD

什么是DDD呢&#xff1f;領域驅動設計[DDD]是一種針對復雜需求的軟件開發方法。將軟件實現與不斷發展的模型聯系起來&#xff0c;專注于核心領域邏輯&#xff0c;而不是基礎設施細節。DDD適用于復雜領域和大規模應用&#xff0c;而不是簡單的CRUD應用。它有助于建立一個靈活、模…

二、通過工廠方法來配置bean

調用靜態工廠方法創建 Bean是將對象創建的過程封裝到靜態方法中. 當客戶端需要對象時, 只需要簡單地調用靜態方法, 而不同關心創建對象的細節. 要聲明通過靜態方法創建的 Bean, 需要在 Bean 的 class 屬性里指定擁有該工廠的方法的類, 同時在 factory-method 屬性里指定工廠方法…

【GlobalMapper精品教程】019:基于DSM提取離散隨機點的高程信息

本文講解在globalmapper中,基于DSM提取離散隨機點的高程信息,配套數據為data019.rar。 文章目錄 1. 離散點創建2. 提取離散點高程信息3. 高程標注1. 離散點創建 本文在ArcGIS中,根據給定的范圍,隨機生成離散點,如下圖: 拓展閱讀: ArcGIS根據范圍創建隨機點教程:【ArcG…

shell腳本注意點

2019獨角獸企業重金招聘Python工程師標準>>> 直接命令行寫腳本的時候&#xff0c;可以用 ; 分割&#xff0c;或 也可以直接回車&#xff0c;然后在繼續寫腳本在使用 方括號[ ] 的時候&#xff0c;里面空格兩邊都必須要有空格&#xff0c;比如 [ $a -gt 3 ] 在方括號…

C語言編程規范--------2 注釋

2.1 注釋的原則 注釋的目的是解釋代碼的目的、功能和采用的方法&#xff0c;提供代碼以外的信息&#xff0c;幫助讀者理解代碼&#xff0c;防止沒必要的重復注釋信息。 示例&#xff1a;如下注釋意義不大。 /* if receive_flag is TRUE */ if (receive_flag) 而如下的注釋則給出…

備戰金九銀十:RabbitMQ有5種工作模式(6)

RabbitMQ是實現了高級消息隊列協議&#xff08;AMQP&#xff09;的開源消息代理軟件&#xff08;亦稱面向消息的中間件&#xff09;。RabbitMQ服務器是用Erlang語言編寫的&#xff0c;而集群和故障轉移是構建在開放電信平臺框架上的。所有主要的編程語言均有與代理接口通訊的客…

【GlobalMapper精品教程】020:Lidar點云數據分類(自動分類、手動分類)案例詳解

航測點云通常跟DSM一致,即包含植被、房屋等信息,必須進行點云分類、過濾,才能生成準確的高程點、等高線和DEM等地形數據。本文以案例的形式詳細講解globalmapper23中點云工具及使用方法。 文章目錄 1. 點云分類2. 創建地面高程格網3. 地形繪制4. 格網轉點云5. 點云抽稀6. 點…

社交網絡圖中結點的“重要性“計算(Dijkstra + SPFA + Floyd + 模板)

題目鏈接&#xff1a; 無 題目大意&#xff1a; 求一個點到其他所有點的最短距離和&#xff0c;保證圖連通。 解題過程&#xff1a; 剛開始用 Floyd 水過的&#xff0c;后來用換了幾種方法&#xff0c;不錯的模板題&#xff0c;Floyd 的時候&#xff0c;要用 vector 存邊&#…

web布局固定寬度+變化寬度實現思路

前言 頁面當中常規布局我想大家都會的&#xff0c;但有些布局是常規布局中實現不了的&#xff0c;比如變寬和固寬結合的&#xff0c;需要實現(300px)&#xff0b;(100%&#xff0d;300px)的兩列布局。以下樣式代碼前提均為盒模型為border-sizing 的前提下。 html部分 <div c…

CSS3 nth 偽類選擇器

考察下面的 HTML 代碼片段&#xff1a; <div><section>section 1</section><section>section 2</section><ul><li>item 1</li><li><ul><li>sub item 1</li><li>sub item 2</li><li>…

RedisCluster的安裝、部署、擴容和 Java客戶端調用

Redis下載 官網地址&#xff1a;http://redis.io/ 中文官網地址&#xff1a;http://www.redis.cn/ 下載地址&#xff1a;http://download.redis.io/releases/ 安裝 # &#xff08;三臺&#xff09;安裝 C 語言需要的 GCC 環境 yum install -y gcc-c yum install -y wget # 下…

【CloudCompare教程】001:CloudCompare中文版下載與安裝圖文教程

CloudCompare是一款功能強大的點云后處理軟件,本文講解CloudCompare中文版下載與安裝方法。 文章目錄 一、CloudCompare下載地址二、CloudCompare安裝教程三、CloudCompare中文設置一、CloudCompare下載地址 官方下載地址:http://www.danielgm.net/cc/release/ 二、CloudComp…

ML.NET相關資源整理

在人工智能領域&#xff0c;無論是機器學習&#xff0c;還是深度學習等&#xff0c;Python編程語言都是絕對的主流&#xff0c;盡管底層都是C實現的&#xff0c;似乎人工智能和C#/F#編程語言沒什么關系。在人工智能的工程實現&#xff0c;通常都是將Python訓練好的人工智能模型…

帶參數的宏替換

帶參數的宏替換因各種需求疊加&#xff0c;替換規則很怪異&#xff1a; 1、首先將實參替換形參&#xff0c;并展開宏 2、如果1步展開后&#xff0c;有#或者##&#xff0c;那么停止替換。 3、如果1步展開后&#xff0c;沒有#或者##&#xff0c;且參數也是宏&#xff0c;那么繼續…