WCF技術剖析之十八:消息契約(Message Contract)和基于消息契約的序列化

在本篇文章中,我們將討論WCF四大契約(服務契約、數據契約、消息契約和錯誤契約)之一的消息契約(Message Contract)。服務契約關注于對服務操作的描述,數據契約關注于對于數據結構和格式的描述,而消息契約關注的是類型成員與消息元素的匹配關系。

我們知道只有可序列化的對象才能通過服務調用在客戶端和服務端之間進行傳遞。到目前為止,我們知道的可序列化類型有兩種:一種是應用了System.SerializableAttribute特性或者實現了System.Runtime.Serialization.ISerializable接口的類型;另一種是數據契約對象。對于基于這兩種類型的服務操作,客戶端通過System.ServiceModel.Dispatcher.IClientMessageFormatter將輸入參數格式化成請求消息,輸入參數全部內容作為有效負載置于消息的主體中;同樣地,服務操作的執行結果被System.ServiceModel.Dispatcher.IDispatchMessageFormatter序列化后作為回復消息的主體。

在一些情況下,具有這樣的要求:當序列化一個對象并生成消息的時候,希望將部分數據成員作為SOAP的報頭,部分作為消息的主體。比如說,我們有一個服務操作采用流的方式進行文件的上載,除了以流的方式傳輸以二進制表示的文件內容外,還需要傳輸一個額外的基于文件屬性的信息,比如文件格式、文件大小等。一般的做法是將傳輸文件內容的流作為SOAP的主體,將其屬性內容作為SOAP的報頭進行傳遞。這樣的功能,可以通過定義消息契約來實現。

一、 消息契約的定義

消息契約和數據契約一樣,都是定義在數據(而不是功能)類型上。不過數據契約旨在定義數據的結構(將數據類型與XSD進行匹配),而消息契約則更多地關注于數據的成員具體在SOAP消息中的表示。消息契約通過以下3個特性進行定義:System.ServiceModel.MessageContractAttributeSystem.ServiceModel.MessageHeaderAttributeSystem.ServiceModel.MessageBodyMemberAttributeMessageContractAttribute應用于類型上,MessageHeaderAttributeMessageBodyMemberAttribute則應用于屬性或者字段成員上,表明相應的數據成員是一個基于SOAP報頭的成員還是SOAP主體的成員。先來簡單介紹一下這3個特性:

1MessageContractAttribute

通過在一個類或者結構(Struct)上應用MessageContractAttribute使之成為一個消息契約。從MessageContractAttribute的定義來看,MessageContractAttribute大體上具有以下兩種類型的屬性成員:

  • ProtectionLevelHasProtectionLevel:表示保護級別,在服務契約中已經對保護級別作了簡單的介紹,WCF中通過System.Net.Security.ProtectionLevel枚舉定義消息的保護級別。一般有3種可選的保護級別:NoneSignEncryptAndSign
  • IsWrappedWrapperNameWrapperNamespaceIsWrapped表述的含義是是否為定義的主體成員(一個或者多個)添加一個額外的根節點。WrapperNameWrapperNamespace則表述該根節點的名稱和命名空間。IsWrappedWrapperNameWrapperNamespace的默認是分別為true、類型名稱和http://tempuri.org/

1: [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = false)]

2: public sealed class MessageContractAttribute : Attribute

3: {

4: //其他成員

5: public bool HasProtectionLevel { get; }

6: public ProtectionLevel ProtectionLevel { get; set; }

7:?

8: public bool IsWrapped { get; set; }

9: public string WrapperName { get; set; }

10: public string WrapperNamespace { get; set; }

11: }

下面的代碼中將Customer類型通過應用MessageContractAttribute使之成為一個消息契約。IDName屬性通過應用MessageHeaderAttribute定義成消息報頭(Header)成員,而Address屬性則通過MessageBodyMemberAttribute定義成消息主體(Body)成員。后面的XML體現的是Customer對象在SOAP消息中的表現形式。

1: [MessageContract]

2: public class Customer

3: {

4: [MessageHeader(Name = "CustomerNo", Namespace = "http://www.artech.com/")]

5: public Guid ID

6: { get; set; }

7:?

8: [MessageHeader(Name = "CustomerName", Namespace = "http://www.artech.com/")]

9: public string Name

10: { get; set; }

11:?

12: [MessageBodyMember(Namespace = "http://www.artech.com/")]

13: public string Address

14: { get; set; }

15: }

1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">

2: <s:Header>

3: <a:Action s:mustUnderstand="1">http://tempuri.org/IOrderManager/ProcessOrder</a:Action>

4: <h:CustomerName xmlns:h="http://www.artech.com/">Foo</h:CustomerName>

5: <h:CustomerNo xmlns:h="http://www.artech.com/">2f62405b-a472-4d1c-8c03-b888f9bd0df9</h:CustomerNo>

6: </s:Header>

7: <s:Body>

8: <Customer xmlns="http://tempuri.org/">

9: <Address xmlns="http://www.artech.com/">#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</Address>

10: </Customer>

11: </s:Body>

12: </s:Envelope>

如果我們將IsWrapped的屬性設為false,那么套在Address節點外的Customer節點將會從SOAP消息中去除。

1: [MessageContract(IsWrapped = false)]

2: public class Customer

3: {

4: //省略成員

5: }

1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">

2: ......

3: <s:Body>

4: <Address xmlns="http://www.artech.com/">#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</Address>

5: </s:Body>

6: </s:Envelope>

我們同樣可以自定義這個主體封套(Wrapper)的命名和命名空間。下面我們就通過將MessageContractAttributeWrapperNameWrapperNamespace屬性設為Custhttp://www.artech.com/

1: [MessageContract(IsWrapped = true, WrapperName = "Cust", WrapperNamespace = "http://www.artech.com/")]

2: public class Customer

3: {

4: //省略成員

5: }

1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">

2: ......

3: <s:Body>

4: <Cust xmlns="http://www.artech.com/">

5: <Address>#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</Address>

6: </Cust>

7: </s:Body>

8: </s:Envelope>

2MessageHeaderAttribute

MessageHeaderAttributeMessageBodyMemberAttribute分別用于定義消息報頭成員和消息主體成員,它們都有一個共同的基類:System.ServiceModel.MessageContractMemberAttributeMessageContractMemberAttribute定義了以下屬性成員:HasProtectionLevelProtectionLevelNameNamespace

1: public abstract class MessageContractMemberAttribute : Attribute

2: {

3: public bool HasProtectionLevel { get; }

4: public ProtectionLevel ProtectionLevel { get; set; }

5:?

6: public string Name { get; set; }

7: public string Namespace { get; set; }

8: }

通過在屬性或者字段成員上應用MessageHeaderAttribute使之成為一個消息報頭成員。MessageHeaderAttribute定義了以下3個屬性,如果讀者對SOAP規范有一定了解的讀者,相信對它們不會陌生。

注:在《WCF技術剖析(卷1)》中的第六章有對SOAP 1.2的基本規范有一個大致的介紹,讀者也可以直接訪問W3C網站下載官方文檔。

  • Actor:表示處理該報頭的目標節點(SOAP Node),SOAP1.1中對應的屬性(Attribute)為actorSOAP 1.2中就是我們介紹的role屬性
  • MustUnderstand:表述ActorSOAP 1.1)或者RoleSOAP 1.2)定義的SOAP節點是否必須理解并處理該節點。對應的SOAP報頭屬性為mustUnderstand
  • Relay:對應的SOAP報頭屬性為relay,表明該報頭是否需要傳遞到下一個SOAP節點

1: [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]

2: public class MessageHeaderAttribute : MessageContractMemberAttribute

3: {

4: public string Actor { get; set; }

5: public bool MustUnderstand { get; set; }

6: public bool Relay { get; set; }

7: }

同樣使用上面定義的Customer消息契約,現在我們相應地修改了ID屬性上的MessageHeaderAtribute設置:MustUnderstand = true, Relay=true, Actor=http://www.w3.org/ 2003/05/soap-envelope/role/ultimateReceiver。實際上將相應的SOAP報頭的目標SOAP節點定義成最終的消息接收者。由于http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiverSOAP 1.2的預定義屬性,所以這個消息契約之后在基于SOAP 1.2的消息版本中有效。后面給出的為對應的SOAP消息。

1: [MessageContract(IsWrapped =true, WrapperNamespace="http://www.artech.com/")]public class Customer

2: {

3: //其他成員

4: [MessageHeader(Name="CustomerNo", Namespace = "http://www.artech.com/" ,MustUnderstand = true, Relay=true, Actor="http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver" )]

5: public Guid ID

6: { get; set; }

7:

8: }

1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">

2: <s:Header>

3: ......

4: <h:CustomerNo s:role="http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver" s:mustUnderstand="1" s:relay="1" xmlns:h="http://www.artech.com/">5330c91a-7fd7-4bf5-ae3e-4ba9bfef3d4d</h:CustomerNo>

5: </s:Header>

6: ......

7: </s:Envelope>

http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiverSOAP1.1中對應的表示為:"http://schemas.xmlsoap.org/soap/actor/ultimateReceiver(具有不同的命名空間)。如果在SOAP 1.1下,ID成員對應的MessageHeaderAttribute應該做如下的改動。從對應的SOAP消息來看,在SOAP 1.2中的role屬性變成了actor屬性。

1: [MessageContract(IsWrapped =true, WrapperNamespace="http://www.artech.com/")]public class Customer

2: {

3: //其他成員

4: [MessageHeader(Name="CustomerNo", Namespace = "http://www.artech.com/" ,MustUnderstand = true, Relay=true, Actor="http://schemas.xmlsoap.org/soap/actor/ultimateReceiver" )]

5: public Guid ID

6: { get; set; }

7: }

1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">

2: <s:Header>

3: ......

4: <h:CustomerNo s:actor="http://schemas.xmlsoap.org/soap/actor/ultimateReceiver" s:mustUnderstand="1" xmlns:h="http://www.artech.com/">e48a8897-c644-49f8-b5e7-cd16be4c75b7</h:CustomerNo>

5: </s:Header>

6: ......

7: </s:Envelope>

3MessageBodyMemberAttribute

MessageBodyMemberAttribute應用于屬性或者字段成員,應用了該特性的屬性或者字段的內容將會出現在SOAP的主體部分。MessageBodyMemberAttribute的定義顯得尤為簡單,僅僅具有一個Order對象,用于控制成員在SOAP消息主體中出現的位置。默認的排序規則是基于字母排序。

可能細心的讀者會問,為什么MessageHeaderAttribute中沒有這樣Order屬性呢?原因很簡單,MessageHeaderAttribute定義的是單個SOAP報頭,SOAP消息報頭集合中的每個報頭元素是次序無關的。而MessageBodyMemberAttribute則是定義SOAP主體的某個元素,主體成員之間的次序也是契約的一個重要組成部分。所以MessageHeaderAttribute不叫MessageHeaderMemberAttribute

1: [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]

2: public class MessageBodyMemberAttribute : MessageContractMemberAttribute

3: {

4: public int Order { get; set; }

5: }

二、實例演示:基于消息契約的方法調用是如何格式化成消息的?

WCF體系中,MessageFormatter負責序列化和反序列化任務(在《WCF技術剖析(卷1)》中的第5章對基于MessageFormatter的序列化機制有詳細的介紹):ClientMessageFormatterDispatchMessageFormatter分別在客戶端和服務端,根據操作的描述(Operation Description),借助于相應的序列化器(Serializer)實現了方法調用與消息之間的轉換。接下來,我將通過一個實實在在的案例程序為大家演示如何通過ClientMessageFormatter將輸入參數轉換為基于當前服務操作的Message。由于本節的主題是消息契約,所以在這里我們將轉換對象限定為消息契約。不過,不論是消息參數還是一般的可序列化對象,其轉換過程都是一樣的。

步驟一:創建消息契約

本案例模擬一個訂單處理的WCF應用,我們首先定義如下一個Order類型。Order是一個消息契約,屬性OrderIDDate通過MessageHeaderAttribute定義成消息報頭,作為主體的Details的類型OrderDetails被定義成集合數據契約。OrderDetails的元素類型是數據契約OrderDetail,代表訂單中每筆產品明細。

1: using System;

2: using System.Collections.Generic;

3: using System.Runtime.Serialization;

4: using System.ServiceModel;

5: namespace Artech.TypedMessage

6: {

7: [MessageContract]

8: public class Order

9: {

10: [MessageHeader(Namespace ="http://www.artech.com/")]

11: public Guid OrderID

12: { get; set; }

13:?

14: [MessageHeader(Namespace ="http://www.artech.com/")]

15: public DateTime Date

16: { get; set; }

17:?

18: [MessageBodyMember]

19: public OrderDetails Details

20: { get; set; }

21:?

22: public override string ToString()

23: {

24: return string.Format("Oder ID: {0}\nDate: {1}\nDetail Count: {2}",this.OrderID,this.Date.ToShortDateString(),this.Details.Count);

25: }

26: }

27:?

28: [CollectionDataContract(ItemName = "Detail",Namespace ="http://www.artech.com/")]

29: public class OrderDetails : List<OrderDetail>

30: { }

31:?

32: [DataContract(Namespace ="http://www.artech.com/")]

33: public class OrderDetail

34: {

35: [DataMember]

36: public Guid ProductID

37: { get; set; }

38:?

39: [DataMember]

40: public int Quantity

41: { get; set; }

42: }

43: }

步驟二:創建MessageFormatter

本例的目的在于重現WCF如何通過ClientMessageFormatter實現將輸入參數序列化成請求消息,以及通過DispatchMessageFormatter實現將請求消息反序列化成輸入參數。根據使用的序列化器的不同,WCF中定義了兩種典型的MessageFormatter:一種是基于DataContractSerializerDataContractSerializerOperationFormatter;另一種則是基于XmlSerializerXmlSerializerOperationFormatter。由于DataContractSerializerOperationFormatter是默認的MessageFormatter,所以我們這個案例就采用DataContractSerializerOperationFormatter

我們的任務就是創建這個DataContractSerializerOperationFormatter。由于這是一個定義在System.ServiceModel.Dispatcher命名空間下的內部(internal)類型,所以我們只能通過反射的機制調用構造函數來創建這個對象。DataContractSerializerOperationFormatter定義了唯一的一個構造函數,3個輸入參數類型分別為:OperationDescriptionDataContractFormatAttributeDataContractSerializerOperationBehavior

1: internal class DataContractSerializerOperationFormatter : OperationFormatter

2: {

3: //其他成員

4: public DataContractSerializerOperationFormatter(OperationDescription description, DataContractFormatAttribute dataContractFormatAttribute, DataContractSerializerOperationBehavior serializerFactory);

5: }

為此我們定義下面一個輔助方法CreateMessageFormatter<TFormatter, TContract>TFormatter代表MessageFormatter的兩個接口:IClientMessageFormatterIDispatchMessageFormatterDataContractSerializerOperationFormatter同時實現了這兩個接口),TContract則是服務契約的類型。參數operationName為當前操作的名稱。代碼不算復雜,主要的流程如下:通過服務契約類型創建ContractDescription,根據操作名稱得到OperationDescription對象。通過反射機制調用DataContractSerializerOperationFormatter的構造函數創建該對象。

1: static TFormatter CreateMessageFormatter<TFormatter, TContract>(string operationName)

2: {

3: ContractDescription contractDesc = ContractDescription.GetContract(typeof(TContract));

4: var operationDescs = contractDesc.Operations.Where(op => op.Name == operationName);

5: if(operationDescs.Count() == 0)

6: {

7: throw new ArgumentException("operationName","Invalid operation name.");

8: }

9: OperationDescription operationDesc = operationDescs.ToArray()[0];

10: string formatterTypeName = "System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";

11: Type formatterType = Type.GetType(formatterTypeName);

12: ConstructorInfo constructor = formatterType.GetConstructor(new Type[] { typeof(OperationDescription), typeof(DataContractFormatAttribute), typeof(DataContractSerializerOperationBehavior) });

13: return (TFormatter)constructor.Invoke(new object[] { operationDesc, new DataContractFormatAttribute(), null });

14: }

MessageFormatter已經創建出來了,序列化與反序列化的問題就很簡單了。為此我定義了以下兩個輔助方法:SerializeRequest<TContract>DeserializeRequest<TContract>,具體實現就是調用創建出來的MessageFormatter的同名方法。

1: static Message SerializeRequest<TContract>(MessageVersion messageVersion, string operationName, params object[] values)

2: {

3: IClientMessageFormatter formatter = CreateMessageFormatter<IClientMessageFormatter, TContract>(operationName);

4: return formatter.SerializeRequest(messageVersion, values);

5: }

6:?

7: static void DeserializeRequest<TContract>(Message message, string operationName, object[] parameters)

8: {

9: IDispatchMessageFormatter formatter = CreateMessageFormatter<IDispatchMessageFormatter, TContract>(operationName);

10: formatter.DeserializeRequest(message, parameters);

11: }

步驟三:通過MessageFormmatter實現消息的格式化

現在我們通過一個簡單的例子來演示通過上面創建的MessageFormatter實現對消息的格式化。由于MessageFormatter進行序列化和反序列化依賴于操作的描述(消息的結構本來就是由操作決定的),為此我們定義了一個服務契約IOrderManager。操作ProcessOrder將消息契約Order作為唯一的參數。

1: using System.ServiceModel;

2: namespace Artech.TypedMessage

3: {

4: [ServiceContract]

5: public interface IOrderManager

6: {

7: [OperationContract]

8: void ProcessOrder(Order order);

9: }

10: }

在下面的代碼中,先調用SerializeRequest<IOrderManager>方法將Order對象進行序列化并生成Message對象,該過程實際上體現了WCF的客戶端框架是如何通過ClientMessageFormatter將操作方法調用連同輸入參數轉換成請求消息的。隨后,調用DeserializeRequest<IOrderManager>方法將Message對象反序列化成Order對象,該過程則代表WCF的服務端框架是如何通過DispatchMessageFormatter將請求消息反序列化成輸入參數的。

1: OrderDetail detail1 = new OrderDetail

2: {

3: ProductID = Guid.NewGuid(),

4: Quantity = 666

5: };

6:?

7: OrderDetail detail2 = new OrderDetail

8: {

9: ProductID = Guid.NewGuid(),

10: Quantity = 999

11: };

12:?

13: Order order = new Order

14: {

15: OrderID = Guid.NewGuid(),

16: Date = DateTime.Today,

17: Details = new OrderDetails { detail1, detail2 }

18: };

19: //模擬WCF客戶端的序列化

20: Message message = SerializeRequest<IOrderManager>(MessageVersion.Default, "ProcessOrder", order);

21: MessageBuffer buffer = message.CreateBufferedCopy(int.MaxValue);

22: WriteMessage(buffer.CreateMessage(), "message.xml");

23:?

24: //模擬WCF服務端的反序列化

25: object[] DeserializedOrder = new object[]{ null };

26: DeserializeRequest<IOrderManager>(buffer.CreateMessage(), "ProcessOrder", DeserializedOrder);

27: Console.WriteLine(DeserializedOrder[0]);

下面的XML表示調用SerializeRequest<IOrderManager>生成的SOAP消息。程序最終的輸出結果也表明了反序列化的成功執行。

1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">

2: <s:Header>

3: <a:Action s:mustUnderstand="1">http://tempuri.org/IOrderManager/ProcessOrder</a:Action>

4: <h:Date xmlns:h="http://www.artech.com/">2008-12-21T00:00:00+08:00</h:Date>

5: <h:OrderID xmlns:h="http://www.artech.com/">cd94a6f0-7e21-4ace-83f7-2ddf061cfbbe</h:OrderID>

6: </s:Header>

7: <s:Body>

8: <Order xmlns="http://tempuri.org/">

9: <Details xmlns:d4p1="http://www.artech.com/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">

10: <d4p1:Detail>

11: <d4p1:ProductID>bc2a186d-569a-4146-9b97-3693248104c0</d4p1:ProductID>

12: <d4p1:Quantity>666</d4p1:Quantity>

13: </d4p1:Detail>

14: <d4p1:Detail>

15: <d4p1:ProductID>72687c23-c2b2-4451-b6c3-da6d040587fc</d4p1:ProductID>

16: <d4p1:Quantity>999</d4p1:Quantity>

17: </d4p1:Detail>

18: </Details>

19: </Order>

20: </s:Body>

21: </s:Envelope>

1: Oder ID: cd94a6f0-7e21-4ace-83f7-2ddf061cfbbe

2: Date: 12/21/2008

3: Detail Count: 2

轉載于:https://www.cnblogs.com/qq260250932/p/5350905.html

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

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

相關文章

【深度學習數據集】常用公開圖片數據集下載

1.MNIST MNIST是一個手寫數字數據庫&#xff0c;它有60000個訓練樣本集和10000個測試樣本集&#xff0c;每個樣本圖像的寬高為28*28。此數據集是以二進制存儲的&#xff0c;不能直接以圖像格式查看&#xff0c;不過很容易找到將其轉換成圖像格式的工具。 最早的深度卷積網絡Le…

常用的幾種卷積神經網絡介紹

常用的幾種卷積神經網絡介紹 標簽&#xff08;空格分隔&#xff09;&#xff1a; 深度學習 這是一篇基礎理論的博客&#xff0c;基本手法是抄、刪、改、查&#xff0c;畢竟介紹這幾個基礎網絡的博文也挺多的&#xff0c;就算是自己的一個筆記吧&#xff0c;以后忘了多看看。主…

計算客 (人人都有極客精神)爆力

人人公司是一家極為鼓舞極客精神的公司&#xff0c;當有重要的項目須要上線但又時間太緊。甚至須要當天上線的時候。往往會掛起海盜旗開啟電子日期顯示。讓大家能夠在對時間有更明白的感知的情況下&#xff0c;同心協力搞定重要的項目。海盜旗下方的電子屏顯示的日期形式為 YYY…

深度學習案例

1. neural-style&#xff1a;利用卷積神經網絡將一幅圖像的內容與另一幅圖像的風格相結合 https://github.com/jcjohnson/neural-style 2.Nerual Doodles&#xff1a;把 2 位的 Doodle 轉成精良的藝術品 https://github.com/alexjc/neural-doodle 3. srez&#xff1a;通過深度…

深度學習圖像標注工具匯總

對于監督學習算法而言&#xff0c;數據決定了任務的上限&#xff0c;而算法只是在不斷逼近這個上限。世界上最遙遠的距離就是我們用同一個模型&#xff0c;但是卻有不同的任務。但是數據標注是個耗時耗力的工作&#xff0c;下面介紹幾個圖像標注工具&#xff1a; Labelme Labe…

UIBarbuttonItem

APPDelegate: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds]; //創建主界面&#xff0c;導航欄的第一個頁面 FirstViewContr…

深度殘差網絡ResNet解析

ResNet在2015年被提出&#xff0c;在ImageNet比賽classification任務上獲得第一名&#xff0c;因為它“簡單與實用”并存&#xff0c;之后很多方法都建立在ResNet50或者ResNet101的基礎上完成的&#xff0c;檢測&#xff0c;分割&#xff0c;識別等領域都紛紛使用ResNet&#x…

Oracle-一個中文漢字占幾個字節?

Oracle 一個中文漢字占用幾個字節 Oracle 一個中文漢字 占用幾個字節&#xff0c;要根據Oracle中字符集編碼決定!!! 1. 如果定義為VARCHAR2(32 CHAR),那么該列最多就可以存儲32個漢字&#xff0c;如果定義字段為VARCHAR2&#xff08;32&#xff09; 或VARCHAR2&#xff08;32 B…

基于深度學習的目標檢測技術演進:R-CNN、Fast R-CNN、Faster R-CNN

object detection我的理解&#xff0c;就是在給定的圖片中精確找到物體所在位置&#xff0c;并標注出物體的類別。object detection要解決的問題就是物體在哪里&#xff0c;是什么這整個流程的問題。然而&#xff0c;這個問題可不是那么容易解決的&#xff0c;物體的尺寸變化范…

iPhone屏幕尺寸/launch尺寸/icon尺寸

屏幕尺寸 6p/6sp 414 X 7366/6s 375 X 6675/5s 320 X 568 4/4s 320 X 480launch尺寸 6p/6sp 1242 X 2208 3x6/6s 750 X 1334 2x5/5s 640 X 1136 2x4/4s 640 X 960 2x仔細觀察會發現l…

CNN的發展歷史(LeNet,Alexnet,VGGNet,GoogleNet,ReSNet)

歡迎轉載&#xff0c;轉載請注明&#xff1a;本文出自Bin的專欄blog.csdn.net/xbinworld。 關于卷積神經網絡CNN&#xff0c;網絡和文獻中有非常多的資料&#xff0c;我在工作/研究中也用了好一段時間各種常見的model了&#xff0c;就想著簡單整理一下&#xff0c;以備查閱之需…

讀取csv格式的數據

1.直接上代碼&#xff0c;關鍵是會用 2.代碼如下&#xff1a; <?php #添加推薦到英文站 $file fopen(code.csv,r); while ($data fgetcsv($file)) { //每次讀取CSV里面的一行內容 //print_r($data); //此為一個數組&#xff0c;要獲得每一個數據&#xff0c;訪問數組下…

如何在VMWare的Ubuntu虛擬機中設置共享文件夾

親測有效&#xff1a;Ubuntu18.04 LTS、虛擬機VMware Workstation 14 Pro 14.1.3 build-9474260、Window7 自己的第一篇博文&#xff0c;由于時&#xff08;shuǐ&#xff09;間&#xff08;png&#xff09;原&#xff08;yǒu&#xff09;因&#xff08;xin&#xff09;&…

容器+AOP實現動態部署(四)

上篇咱們介紹了容器和AOP的結合&#xff0c;結合后怎樣將對象增強服務并沒有過多的說明&#xff0c;這里將詳細說明怎樣將對象 進行增強 &#xff0c;達到一個一對多和多對多的增強方式 先從簡單的方式說起 /** *JDK代理類&#xff0c;實現動態調用對象方法 */ public class JD…

caffe專題五——回歸中——檢測框架

https://blog.csdn.net/runner668/article/details/80436850

深入理解卷積層,全連接層的作用意義

有部分內容是轉載的知乎的&#xff0c;如有侵權&#xff0c;請告知&#xff0c;刪除便是&#xff0c;但由于是總結的&#xff0c;所以不一一列出原作者是who。 再次感謝&#xff0c;也希望給其他小白受益。 首先說明&#xff1a;可以不用全連接層的。 理解1&#xff1a; 卷…

用ionic快速開發hybird App(已附源碼,在下面+總結見解)

用ionic快速開發hybird App&#xff08;已附源碼,在下面總結見解&#xff09; 1.ionic簡介 ionic 是用于敏捷開發APP的解決方案。核心思路是&#xff1a;利用成熟的前端開發技術&#xff0c;來寫UI和業務邏輯。也就是說&#xff0c;就是一個H5網站&#xff0c;這個區別于react-…

為什么要使用工廠模式

工廠的作用相當于幫助我們完成實例化的操作。 優勢1&#xff1a;一般在代碼中&#xff0c;實例化一個類A是直接new A&#xff08;&#xff09;&#xff0c;假如類A是一個完全獨立的類&#xff0c;沒有相似類&#xff0c;則沒有必要使用工廠模式&#xff0c;直接new A&#xff…

css各兼容應該注意的問題

1.div布局在ie瀏覽器和chrome瀏覽器&#xff0c;firefox瀏覽器不同&#xff0c;不如在div里面嵌套3個div&#xff0c;分別左中右&#xff0c;左邊div的pading和margin在ie8以上都是幾乎相同&#xff0c;ie8以下做內邊距x2&#xff0c;在中間的div在chrome和fierfox中默認在左邊…

轉 C++宏定義詳解

來自&#xff1a;傳送門 C宏定義詳解 一、#define的基本用法 #define是C語言中提供的宏定義命令&#xff0c;其主要目的是為程序員在編程時提供一定的方便&#xff0c;并能在一定程度上提高程序的運行效率&#xff0c;但學生在學習時往往不能 理解該命令的本質&#xff0c;總是…