【AutoMapper官方文件】DTO與Domin Model相互轉換(下)
寫在前面
AutoMapper目錄:
本篇目錄:
關於AutoMapper寫到這基本的東西都差不多了,上一篇定義為靈活配置篇,本篇可以定義為擴充套件應用篇,加一些補充,關於AutoMapper的專案應用,網上找了幾篇英文文章,雖然看不懂,但是程式碼是相通的,感覺很不錯,主要是EntityFramework中運用AutoMapper,資料訪問中使用AutoMapper,有支援的,也有反對的,也有提出建議的,自己也正在摸索,希望有機會寫篇文章和大家分享下。
插一句:寫這些東西,看的人真的很少,還不如像前幾天大家寫篇水文,來討論下C#的好壞增加點人氣,呵呵,但是如果是這種思想來程式設計真是不可饒恕,寫這種文章的目的不一定是分享給別人,也是對自己學習的另一種修煉,畢竟肚子沒有什麼東西,是寫不出來,也是在逼迫自己去學習,當去學習一點東西后,發現
Mapping Inheritance-對映繼承
關於對映繼承,其實在“Lists and Array-集合和陣列”這一節點有提到,但是隻是說明下AutoMapper解決對映繼承所使用的方式,這邊我們說下關於AutoMapper在對映繼承中的一些特性,比如下面轉換示例:
1 public class Order { } 2 public class OnlineOrder : Order { } 3 public class MailOrder : Order { }4 5 public class OrderDto { } 6 public class OnlineOrderDto : OrderDto { } 7 public class MailOrderDto : OrderDto { }
源物件和目標物件存在繼承關係,和“Lists and Array”節點裡面的的示例一樣,我們首先要配置AutoMapper,新增型別對映關係和依賴關係,如下:
1 //配置 AutoMapper 2 Mapper.CreateMap<Order, OrderDto>()3 .Include<OnlineOrder, OnlineOrderDto>() 4 .Include<MailOrder, MailOrderDto>(); 5 Mapper.CreateMap<OnlineOrder, OnlineOrderDto>(); 6 Mapper.CreateMap<MailOrder, MailOrderDto>();
關於這三段程式碼的意義,在“Lists and Array”節點中也有說明,如果我們註釋掉第一段程式碼,我們在做派生類對映轉換的時候就會報錯,如果我們把下面兩段程式碼去掉,我們在做派生類對映轉換的時候就會對映到基類,說明第一段程式碼的意義是,不僅僅包含Order到OrderDto之間的型別對映,還包含Order與OrderDto所有派生類之間的對映,但是隻是宣告,如果要對派生類之間進行型別對映轉換,就還得需要建立派生類之間的型別對映關係。
我們在“Lists and Array”節點中這樣執行型別對映轉換:
1 var destinations = Mapper.Map<ParentSource[], ParentDestination[]>(sources);
上面這段轉換程式碼是指定了源泛型型別(Source)和目標泛型型別型別(Dest),所以AutoMapper會根據指定的型別就可以進行轉換了,前提是型別對映關係配置正確,要不然就會報“AutoMapperConfigurationException”異常。如果我們不指定目標資料型別,然後就行轉換會怎樣呢?比如下面轉換:
1 var order = new OnlineOrder(); 2 var mapped = Mapper.Map(order, order.GetType(), typeof(OrderDto)); 3 Console.WriteLine("mapped Type:" + mapped.GetType());
轉換效果:
程式碼中我們並沒有指定目標資料型別,只是指定一個派生類的基類,如果按照我們的理解,這段程式碼執行的結果應該是:轉換結果mapped物件的型別應該是OrderDto型別,但是確是我們希望想要的OnlineOrderDto型別,雖然我們沒有指定目標型別為OnlineOrderDto,但是這一切AutoMapper都幫你做了,就是說AutoMapper會自動判斷目標型別與源資料型別存在的關係,並找出最合適的派生類型別。
Queryable Extensions (LINQ)-擴充套件查詢表示式
注:關於Entity Framework中資料型別對映正在研究,網上找了很多英文文章,還在消化中,這一節點只是簡單的說下AutoMapper查詢表示式的用法,過幾天再整理一篇關於實體框架中運用資料型別對映的文章,功力不夠,還請包涵。
當我們使用Entity Framework與AutoMapper結合進行查詢物件轉換的時候,使用Mapper.Map方法,就會發現查詢結果物件中的所有屬性和目標屬性物件屬性都會轉換,當然你也可以在查詢結果集中構建一個所需結果的示例,但是這樣做並不是可取的,AutoMapper的作者擴充套件了QueryableExtensions,使得我們在查詢的時候就可以實現轉換,比如下面示例:
1 public class OrderLine 2 { 3 public int Id { get; set; } 4 public int OrderId { get; set; } 5 public Item Item { get; set; } 6 public decimal Quantity { get; set; } 7 } 8 public class Item 9 { 10 public int Id { get; set; } 11 public string Name { get; set; } 12 } 13 14 public class OrderLineDTO 15 { 16 public int Id { get; set; } 17 public int OrderId { get; set; } 18 public string Item { get; set; } 19 public decimal Quantity { get; set; } 20 } 21 22 public List<OrderLineDTO> GetLinesForOrder(int orderId) 23 { 24 Mapper.CreateMap<OrderLine, OrderLineDTO>() 25 .ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name)); 26 27 using (var context = new orderEntities()) 28 { 29 return context.OrderLines.Where(ol => ol.OrderId == orderId) 30 .Project().To<OrderLineDTO>().ToList(); 31 } 32 }
程式碼中的.Project().To就是擴充套件的查詢表示式,詳細表達式程式碼:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Linq.Expressions; 5 using System.Reflection; 6 using System.Text.RegularExpressions; 7 8 namespace DTO_AutoMapper使用詳解 9 { 10 public static class QueryableExtensions 11 { 12 public static ProjectionExpression<TSource> Project<TSource>(this IQueryable<TSource> source) 13 { 14 return new ProjectionExpression<TSource>(source); 15 } 16 } 17 18 public class ProjectionExpression<TSource> 19 { 20 private static readonly Dictionary<string, Expression> ExpressionCache = new Dictionary<string, Expression>(); 21 22 private readonly IQueryable<TSource> _source; 23 24 public ProjectionExpression(IQueryable<TSource> source) 25 { 26 _source = source; 27 } 28 29 public IQueryable<TDest> To<TDest>() 30 { 31 var queryExpression = GetCachedExpression<TDest>() ?? BuildExpression<TDest>(); 32 33 return _source.Select(queryExpression); 34 } 35 36 private static Expression<Func<TSource, TDest>> GetCachedExpression<TDest>() 37 { 38 var key = GetCacheKey<TDest>(); 39 40 return ExpressionCache.ContainsKey(key) ? ExpressionCache[key] as Expression<Func<TSource, TDest>> : null; 41 } 42 43 private static Expression<Func<TSource, TDest>> BuildExpression<TDest>() 44 { 45 var sourceProperties = typeof(TSource).GetProperties(); 46 var destinationProperties = typeof(TDest).GetProperties().Where(dest => dest.CanWrite); 47 var parameterExpression = Expression.Parameter(typeof(TSource), "src"); 48 49 var bindings = destinationProperties 50 .Select(destinationProperty => BuildBinding(parameterExpression, destinationProperty, sourceProperties)) 51 .Where(binding => binding != null); 52 53 var expression = Expression.Lambda<Func<TSource, TDest>>(Expression.MemberInit(Expression.New(typeof(TDest)), bindings), parameterExpression); 54 55 var key = GetCacheKey<TDest>(); 56 57 ExpressionCache.Add(key, expression); 58 59 return expression; 60 } 61 62 private static MemberAssignment BuildBinding(Expression parameterExpression, MemberInfo destinationProperty, IEnumerable<PropertyInfo> sourceProperties) 63 { 64 var sourceProperty = sourceProperties.FirstOrDefault(src => src.Name == destinationProperty.Name); 65 66 if (sourceProperty != null) 67 { 68 return Expression.Bind(destinationProperty, Expression.Property(parameterExpression, sourceProperty)); 69 } 70 71 var propertyNames = SplitCamelCase(destinationProperty.Name); 72 73 if (propertyNames.Length == 2) 74 { 75 sourceProperty = sourceProperties.FirstOrDefault(src => src.Name == propertyNames[0]); 76 77 if (sourceProperty != null) 78 { 79 var sourceChildProperty = sourceProperty.PropertyType.GetProperties().FirstOrDefault(src => src.Name == propertyNames[1]); 80 81 if (sourceChildProperty != null) 82 { 83 return Expression.Bind(destinationProperty, Expression.Property(Expression.Property(parameterExpression, sourceProperty), sourceChildProperty)); 84 } 85 } 86 } 87 88 return null; 89 } 90 91 private static string GetCacheKey<TDest>() 92 { 93 return string.Concat(typeof(TSource).FullName, typeof(TDest).FullName); 94 } 95 96 private static string[] SplitCamelCase(string input) 97 { 98 return Regex.Replace(input, "([A-Z])", " $1", RegexOptions.Compiled).Trim().Split(' '); 99 } 100 } 101 }View Code
我們在前幾節點中說的自定義對映規則,其實也是屬於查詢表示式的一種,結合實體框架可以簡單的應用下,比如我們要對映到DTO中一個Count屬性,來計算查詢結果集中的數量,如下面程式碼:
1 Mapper.CreateMap<Customer, CustomerDto>() 2 .ForMember(d => d.FullName, opt => opt.MapFrom(c => c.FirstName + " " + c.LastName)) 3 .ForMember(d => d.TotalContacts, opt => opt.MapFrom(c => c.Contacts.Count()));
LINQ支援聚合查詢,AutoMapper支援LINQ的擴充套件方法。在自定義對映中,如果我們講屬性名稱TotalContacts改為ContactsCount,AutoMapper將自動匹配到COUNT()擴充套件方法和LINQ提供程式將轉化計數到相關子查詢彙總子記錄。AutoMapper還可以支援複雜的聚合和巢狀的限制,如果LINQ提供的表示式支援它,例如下面程式碼:
1 Mapper.CreateMap<Course, CourseModel>() 2 .ForMember(m => m.EnrollmentsStartingWithA, 3 opt => opt.MapFrom(c => c.Enrollments.Where(e => e.Student.LastName.StartsWith("A")).Count()));
上面計算的是每門課程,學生名字開頭為“A”的學生數量。
不是所有的對映選項都支援表示式,因為它必須有LINQ的支援,支援的有:
- MapFrom
- Ignore
不支援的有:
- Condition
- DoNotUseDestinationValue
- SetMappingOrder
- UseDestinationValue
- UseValue
- ResolveUsing
- Any calculated property on your domain object
Configuration-配置
Profile-修飾
AutoMapper提供了個性化設定Profile,使得我們轉換後的資料格式可以多變,當然還可以配置全域性格式等等,需要繼承自Profile,並重寫Configure方法,然後在AutoMapper初始化的時候,講自定義配置新增到對映配置中,如下面示例:
1 public class Order 2 { 3 public decimal Amount { get; set; } 4 } 5 public class OrderListViewModel 6 { 7 public string Amount { get; set; } 8 } 9 public class OrderEditViewModel 10 { 11 public string Amount { get; set; } 12 } 13 public class MoneyFormatter : ValueFormatter<decimal> 14 { 15 protected override string FormatValueCore(decimal value) 16 { 17 return value.ToString("c"); 18 } 19 } 20 public class ViewModelProfile : Profile 21 { 22 protected override void Configure() 23 { 24 CreateMap<Order, OrderListViewModel>(); 25 ForSourceType<decimal>().AddFormatter<MoneyFormatter>(); 26 } 27 }
先建立了一個MoneyFormatter字元格式化類,然後建立ViewModelProfile配置類,在Configure方法中,新增型別對映關係,ForSourceType指的是講元資料型別新增格式化,配置使用程式碼:
1 public void Example() 2 { 3 var order = new Order { Amount = 50m }; 4 //配置 AutoMapper 5 Mapper.Initialize(cfg => 6 { 7 cfg.AddProfile<ViewModelProfile>(); 8 cfg.CreateMap<Order, OrderEditViewModel>(); 9 }); 10 //執行 mapping 11 var listViewModel = Mapper.Map<Order, OrderListViewModel>(order); 12 var editViewModel = Mapper.Map<Order, OrderEditViewModel>(order); 13 14 Console.WriteLine("listViewModel.Amount:" + listViewModel.Amount); 15 Console.WriteLine("editViewModel.Amount:" + editViewModel.Amount); 16 }
可以看到在Mapper.Initialize初始化的時候,把ViewModelProfile新增到AutoMapper配置中,泛型型別引數必須是Profile的派生類,因為我們在ViewModelProfile的Configure方法中添加了Order到OrderListViewModel型別對映關係,所以我們再初始化的時候就不需要添加了,轉換效果:
Naming Conventions-命名約定
在“Flattening-複雜到簡單”節點中,我們說到AutoMapper對映轉換遵循PascalCase(帕斯卡命名規則),所以我們在型別名稱命名要按照PascalCase進行命名,除了預設的命名規則,AutoMapper還提供了一種命名規則,如下:
1 Mapper.Initialize(cfg => { 2 cfg.SourceMemberNamingConvention = new LowerUnderscoreNamingConvention(); 3 cfg.DestinationMemberNamingConvention = new PascalCaseNamingConvention(); 4 });
SourceMemberNamingConvention表示源資料型別命名規則,DestinationMemberNamingConvention表示目標資料型別命名規則,LowerUnderscoreNamingConvention和PascalCaseNamingConvention是AutoMapper提供的兩個命名規則,前者命名是小寫幷包含下劃線,後者就是帕斯卡命名規則,所以對映轉換的效果是:property_name -> PropertyName。
當然除了在AutoMapper初始化的時候配置命名規則,也可以在Profile中新增全域性配置,如下:
1 public class OrganizationProfile : Profile 2 { 3 protected override void Configure() 4 { 5 SourceMemberNamingConvention = new LowerUnderscoreNamingConvention(); 6 DestinationMemberNamingConvention = new PascalCaseNamingConvention(); 7 } 8 }
Conditional Mapping-條件對映
AutoMapper允許在型別對映之前新增條件,例如下面示例:
1 public class Foo 2 { 3 public int baz { get; set; } 4 } 5 public class Bar 6 { 7 public uint baz { get; set; } 8 } 9 public void Example() 10 { 11 var foo = new Foo { baz = 1 }; 12 //配置 AutoMapper 13 Mapper.CreateMap<Foo, Bar>() 14 .ForMember(dest => dest.baz, opt => opt.Condition(src => (src.baz >= 0))); 15 //執行 mapping 16 var result = Mapper.Map<Foo, Bar>(foo); 17 18 Console.WriteLine("result.baz:" + result.baz); 19 }
上面示例表示當源資料baz大於0的時候,才能執行對映,關鍵字是Condition,Condition方法接受一個Func<TSource, bool>型別引數,注意已經指定返回值為bool型別,方法簽名:
1 // 2 // 摘要: 3 // Conditionally map this member 4 // 5 // 引數: 6 // condition: 7 // Condition to evaluate using the source object 8 void Condition(Func<TSource, bool> condition);
轉換效果:
AutoMapper版本變化點
在AutoMapper1.1版本中,如果我們要對型別巢狀對映中加入自定義型別對映,比如下面示例:
1 Mapper.CreateMap<Order, OrderDto>() 2 .Include<OnlineOrder, OnlineOrderDto>() 3 .Include<MailOrder, MailOrderDto>() 4 .ForMember(o=>o.Id, m=>m.MapFrom(s=>s.OrderId)); 5 Mapper.CreateMap<OnlineOrder, OnlineOrderDto>() 6 .ForMember(o=>o.Id, m=>m.MapFrom(s=>s.OrderId)); 7 Mapper.CreateMap<MailOrder, MailOrderDto>() 8 .ForMember(o=>o.Id, m=>m.MapFrom(s=>s.OrderId));
可以看出,我們需要在每個型別對映的地方要加:.ForMember(o=>o.Id, m=>m.MapFrom(s=>s.OrderId));但是Order、OnlineOrder和MailOrder存在繼承關係,難道我們如果再加一個派生類對映,就得加一段這樣程式碼,這樣就會程式碼就會變得難以維護。在AutoMapper2.0版本中解決了這一問題,只需要下面這樣配置就可以了:
1 Mapper.CreateMap<Order, OrderDto>() 2 .Include<OnlineOrder, OnlineOrderDto>() 3 .Include<MailOrder, MailOrderDto>() 4 .ForMember(o=>o.Id, m=>m.MapFrom(s=>s.OrderId)); 5 Mapper.CreateMap<OnlineOrder, OnlineOrderDto>(); 6 Mapper.CreateMap<MailOrder, MailOrderDto>();
型別對映優先順序
- Explicit Mapping (using .MapFrom())-顯式對映:優先順序最高,我們使用MapFrom方法定義對映規則,比如:.ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date))
- Inherited Explicit Mapping-繼承的顯式對映:就是存在繼承關係的MapFrom定義對映規則對映。
- Ignore Property Mapping-忽略屬性對映:使用Ignore方法指定屬性忽略對映,比如:Mapper.CreateMap<Source, Destination>().ForMember(dest => dest.SomeValuefff, opt => opt.Ignore());
- Convention Mapping (Properties that are matched via convention)-公約對映:公約對映即符合PascalCase命名規則的對映。如Source類中有Value屬性,Dest類中也有Value屬性,Source和Dest對映關係即是公約對映。
- Inherited Ignore Property Mapping-繼承的忽略屬性對映:優先順序最低,就是存在繼承關係的忽略屬性對映。
我們舉個簡單示例來說明下對映優先順序:
1 //Domain Objects 2 public class Order { } 3 public class OnlineOrder : Order 4 { 5 public string Referrer { get; set; } 6 } 7 public class MailOrder : Order { } 8 9 //Dtos 10 public class OrderDto 11 { 12 public string Referrer { get; set; } 13 } 14 public void Example2() 15 { 16 //配置 AutoMapper 17 Mapper.CreateMap<Order, OrderDto>() 18 .Include<OnlineOrder, OrderDto>() 19 .Include<MailOrder, OrderDto>() 20 .ForMember(o => o.Referrer, m => m.Ignore()); 21 Mapper.CreateMap<OnlineOrder, OrderDto>(); 22 Mapper.CreateMap<MailOrder, OrderDto>(); 23 24 //執行 Mapping 25 var order = new OnlineOrder { Referrer = "google" }; 26 var mapped = Mapper.Map(order, order.GetType(), typeof(OrderDto)); 27 }
轉換後mapped物件的Referrer屬性值為“google”,但是你發現我們在配置對映規則的時候,不是把Referrer屬性給Ignore(忽略)了嗎?因為OnlineOrder的ReferrerOrderDto的Referrer屬性符合PascalCase命名規則,即是公約對映,雖然忽略屬性對映的優先順序比公約對映高,但是上面示例中Order和OnlineOrder存在繼承關係,即是繼承的忽略屬性對映,所以優先順序比公約對映要低。
後記
如果你覺得本篇文章對你有所幫助,請點選右下部“推薦”,^_^
參考資料:
相關推薦
【AutoMapper官方文件】DTO與Domin Model相互轉換(下)
寫在前面 AutoMapper目錄: 本篇目錄: 關於AutoMapper寫到這基本的東西都差不多了,上一篇定義為靈活配置篇,本篇可以定義為擴充套件應用篇,加一些補充,關於AutoMapper的專案應用,網上找了幾篇英文文章,雖然看不懂,但是程式碼是相通的,感覺很不錯,主要是Enti
【AutoMapper官方文件】DTO與Domin Model相互轉換(上)
寫在前面 AutoMapper目錄: 本篇目錄: 上一篇《【道德經】漫談實體、物件、DTO及AutoMapper的使用 》,因為內容寫的有點跑偏,關於AutoMapper的使用最後只是簡單寫了下,很明顯這種簡單的使用方式不能滿足專案中複雜的需要,網上找了下AutoMapper相關文件
【AutoMapper官方文件】DTO與Domin Model相互轉換(中)
寫在前面 AutoMapper目錄: 本篇目錄: 隨著AutoMapper的學習深入,發現AutoMapper在物件轉換方面(Object-Object Mapping)還蠻強大的,當時使用AutoMapper的場景是DTO與Domin Model相互轉換,所以文章的標題就是這個(標
【android官方文件】android AIDL
概述 AIDL(安卓介面解釋語言)和其他的IDLs類似。可以定義程式介面讓客戶端和service進行跨程序的通訊(IPC)。在android中,一個程序通常不能訪問另一個程序的記憶體。所以,他們的物件需要被分解成更原始的單位,直到系統可以理解,並且集結這些物件穿
【Android官方文件】翻譯Android官方文件-Activities(一)
Activity是可以給使用者提供互動操作的程式元件,例如打電話,拍照,傳送郵件,抑或者是顯示地圖。通常視窗會填滿螢幕,但是也可以做到比螢幕小或者是懸浮在視窗頂部。 App通常由多個Activities組成,它們之間支援相互跳轉。一般情況下,每個Activit
【pytest官方文件】解讀fixtures - 1.什麼是fixtures
在深入瞭解fixture之前,讓我們先看看什麼是`測試`。 ### 一、測試的構成 其實說白了,測試就是在特定的環境、特定的場景下、執行特定的行為,然後確認結果與期望的是否一致。 就拿最常見的登入來說,完成一次正常的登入場景,需要可用的測試環境,可以正常登入的賬號和密碼。 然後,用這個賬號密碼進行登入操
【pytest官方文件】解讀fixtures - 2. fixtures的呼叫方式
既然fixtures是給執行測試做準備工作的,那麼pytest如何知道哪些測試函式 或者 fixtures要用到哪一個fixtures呢? 說白了,就是fixtures的呼叫。 ### 一、測試函式宣告傳參請求fixture 測試函式通過將fixture宣告為引數來請求fixture。 ``` def te
【pytest官方文件】解讀fixtures - 8. yield和addfinalizer的區別(填坑)
在[上一章](https://www.cnblogs.com/pingguo-softwaretesting/p/14479170.html)中,文末留下了一個坑待填補,疑問是這樣的: 目前從官方文件中看到的是 ``` We have to be careful though, because pytest
Pulsar官方文件翻譯-入門必看-概念和架構-(一)概覽(Pulsar Overview)
官網原文標題《Concepts and Architecture--Pulsar Overview》 翻譯時間:2018-09-28 譯者:本文介紹了Pulsar的起源和現狀,以及主要特性。 後續閱讀:《Messaging Concepts》 譯者序言: 由
【小白學PyTorch】21 Keras的API詳解(下)池化、Normalization層
文章來自微信公眾號:【機器學習煉丹術】。作者WX:cyx645016617. 參考目錄: [toc] 下篇的內容中,主要講解這些內容: - 四個的池化層; - 兩個Normalization層; ## 1 池化層 和卷積層相對應,每一種池化層都有```1D,2D,3D```三種類型,這裡主要介紹2D處理影象
unicode與GB2312的相互轉換(js)
上回說到,我們用C語言輸出了一張GB2312的全部字元表……同時也說,有了這個,我們就能實現使用js進行unicode和GB2312之間的轉碼了……再加上前回(其實是幾年之前)說到,用js沒有內建函式實現這兩者的轉碼,如果用到,一般都是藉助於vbs……這使得我的BF直譯器(
xml與bean間相互轉換(補充)
今天x被stream對xmlnode的屬性(attribute)解析的問題一直困擾著,查詢了很久都告知我要手寫一個Converter,那豈不意味著我每解析一個xml檔案,就得寫一次Converter,那樣太腦殘了,最後搜尋到其實可以用註解解決這個問題 XStream常用註解
AutoMapper官方文件(二)【升級指南】
初始化 您現在必須使用Mapper.Initialize或new MapperConfiguration()來初始化AutoMapper。如果您希望保持靜態使用,請使用Mapper.Initialize。 如果你有很多的Mapper.CreateMap呼叫,把它們移動到一個Profile,或者Mapper
【python】numpy庫陣列拼接np.concatenate官方文件詳解與例項
在實踐過程中,會經常遇到陣列拼接的問題,基於numpy庫concatenate是一個非常好用的陣列操作函式。 1、concatenate((a1, a2, …), axis=0)官方文件詳解 concatenate(...) concatenate(
【pySerial3.4官方文件】6、示例
示例 Miniterm Miniterm現在可用作模組而不是示例。有關詳細資訊,請參閱serial.tools.miniterm。 miniterm.py miniterm計劃。 setup-miniterm-py2exe.py 這是Windows的py2exe安
【pySerial3.4官方文件】4、工具
工具 serial.tools.list_ports 可以執行此模組以獲取埠列表()。它還包含以下功能。python -m serial.tools.list_ports serial.tools.list_ports.comports(include_l
【pySerial3.4官方文件】3、pySerial API
pySerial API 類 本地埠 類serial.Serial __init__(port = None,baudrate = 9600,bytesize = EIGHTBITS,parity = PARITY_NONE,stopbits = STOPBITS_ONE
【pySerial3.4官方文件】2、簡介
簡介 開啟串列埠 開啟“9600,8,N,1”的埠,沒有超時: >>> import serial >>> ser = serial.Serial('/dev/ttyUSB0') # open serial port >>> pri
【Gradle官方文件翻譯】起步2:建立構建掃描
構建掃描是對構建的可分享的專門記錄,可以看到“構建中發生了那些行為以及為什麼會發生這種行為”。通過在專案中使用構建掃描外掛,開發者可以免費地在https://scans.gradle.com/上釋出構建掃描。 將要建立的 本文會展示如何在不對任何構建指令碼進行
【cocos2d-js官方文件】十七、事件分發機制
簡介 遊戲開發中一個很重要的功能就是互動,如果沒有與使用者的互動,那麼遊戲將變成動畫,而處理使用者互動就需要使用事件監聽器了。 總概: 事件監聽器(cc.EventListener) 封裝使用者的事件處理邏輯事件管理器(cc.eventManager) 管理使用者註