c#從Attribute(特性)類到System.Runtime.InteropServices名稱空間理解託管/非託管——c#
第一篇 特性和屬性的對比
C#: +(特性 ) + Attitude C#(類)前面或者(方法)前面 (中括號)定義
首先要說的是,可能一些剛接觸C#的朋友常常容易把屬性(Property)跟特性(Attribute)弄混淆,其實這是兩種不同的東西。屬性就是面向物件思想裡所說的封裝在類裡面的資料欄位,其形式為:
1:publicclassHumanBase
2:{
3:publicstringName{get;set;}
4:publicintAge{get;set;}
5:publicintGender{get;set;}
6:}
在HumanBase這個類裡出現的欄位都叫屬性(Property),而特性(Attribute)又是怎樣的呢?
1:[Serializable]
2:publicclassHumanBase
3:{
4:publicstringName{get;set;}
5:publicintAge{get;set;}
6:publicintGender{get;set;}
7:}
簡單地講,我們在HumanBase類宣告的上一行加了一個[Serializable],這就是特性(Attribute),它表示HumanBase是可以被序列化的,這對於網路傳輸是很重要的,不過你不用擔心如何去理解它,如何理解就是我們下面要探討的。
C#的特性可以應用於各種型別和成員。前面的例子將特性用在類上就可以被稱之為“類特性”,同理,如果是加在方法宣告前面的就叫方法特性。無論它們被用在哪裡,無論它們之間有什麼區別,特性的最主要目的就是自描述。並且因為特性是可以由自己定製的,而不僅僅侷限於.NET提供的那幾個現成的,因此給C#程式開發帶來了相當大的靈活性和便利。
我們還是借用生活中的例子來介紹C#的特性機制吧。
假設有一天你去坐飛機,你就必須提前去機場登機處換登機牌。登機牌就是一張紙,上面寫著哪趟航班、由哪裡飛往哪裡以及你的名字、座位號等等資訊,其實,這就是特性。它不需要你生理上包含這些屬性(人類出現那會兒還沒飛機呢),就像上面的HumanBase類沒有IsSerializable這樣的屬性,特性只需要在類或方法需要的時候加上去就行了,就像你不總是在天上飛一樣。
當我們想知道HumanBase是不是可序列化的,可以通過:
1:staticvoidMain(string[]args)
2:{
3:Console.WriteLine(typeof(HumanBase).IsSerializable);
4:
5:Console.ReadLine();
6:}
拿到了登機牌,就意味著你可以合法地登機起飛了。但此時你還不知道你要坐的飛機停在哪裡,不用擔心,地勤人員會開車送你過去,但是他怎麼知道你是哪趟航班的呢?顯然還是通過你手中的登機牌。所以,特性最大的特點就是自描述。
既然是起到描述的作用,那目的就是在於限定。就好比地勤不會把你隨便拉到一架飛機跟前就扔上去了事,因為標籤上的說明資訊就是起到限定的作用,限定了目的地、乘客和航班,任何差錯都被視為異常。如果前面的HumanBase不加上Serializable特性就不能在網路上傳輸。
我們在順帶來介紹一下方法特性,先給HumanProperty加上一個Run方法:
1:[Serializable]
2:publicclassHumanPropertyBase
3:{
4:publicstringName{get;set;}
5:publicintAge{get;set;}
6:publicintGender{get;set;}
7:
8:publicvoidRun(intspeed)
9:{
10://Runningisgoodforhealth.
11:}
12:}
只要是個四肢健全、身體健康的人就可以跑步,那這麼說,跑步就是有前提條件的,至少是四肢健全,身體健康。由此可見,殘疾人和老年人如果跑步就會出問題。假設一個HumanBase的物件代表的是一位耄耋老人,如果讓他當劉翔的陪練,那就直接光榮了。如何避免這樣的情況呢,我們可以在Run方法中加一段邏輯程式碼,先判斷Age大小,如果小於2或大於60直接拋異常,但是2-60歲之間也得用Switch來分年齡階段地判斷speed引數是否合適,那麼邏輯就相當臃腫。簡而言之,如何用特性表示一個方法不能被使用呢?OK,herewego:
1:[Obsolete("I'msoold,don'tkillme!",true)]
2:publicvirtualvoidRun(intspeed)
3:{
4://Runningisgoodforhealth.
5:}
上面大致介紹了一下特性的使用與作用,接下來我們要向大家展示的是如何通過自定義特性來提高程式的靈活性,如果特性機制僅僅能使用.NET提供的那幾種特性,不就太不過癮了麼。
首先,特性也是類。不同於其它類的是,特性都必須繼承自System.Attribute類,否則編譯器如何知道誰是特性誰是普通類呢。當編譯器檢測到一個類是特性的時候,它會識別出其中的資訊並存放在元資料當中,僅此而已,編譯器並不關心特性說了些什麼,特性也不會對編譯器起到任何作用,正如航空公司並不關心每個箱子要去哪裡,只有箱子的主人和搬運工才會去關心這些細節。假設我們現在就是航空公司的管理人員,需要設計出前面提到的登機牌,那麼很簡單,我們先看看最主要的資訊有哪些:
1:publicclassBoardingCheckAttribute:Attribute
2:{
3:publicstringID{get;privateset;}
4:publicstringName{get;privateset;}
5:publicintFlightNumber{get;privateset;}
6:publicintPostionNumber{get;privateset;}
7:publicstringDeparture{get;privateset;}
8:publicstringDestination{get;privateset;}
9:}
我們簡單列舉這些屬性作為航空公司登機牌上的資訊,用法和前面的一樣,貼到HumanBase上就行了,說明此人具備登機資格。這裡要簡單提一下,你可能已經注意到了,在使用BoardingCheckAttribute的時候已經把Attribute省略掉了,不用擔心,這樣做是對的,因為編譯器預設會自己加上然後查詢這個屬性類的。哦,等一下,我突然想起來他該登哪架飛機呢?顯然,在這種需求下,我們的特性還沒有起到應有的作用,我們還的做點兒工作,否則乘客面對一張空白的機票一定會很迷茫。
於是,我們必須給這個特性加上建構函式,因為它不僅僅表示登機的資格,還必須包含一些必要的資訊才行:
1:publicBoardingCheckAttribute(stringid,stringname,stringflightNumber,stringpositionNumber,stringdeparture,stringdestination)
2:{
3:this.ID=id;
4:this.Name=name;
5:this.FlightNumber=flightNumber;
6:this.PositionNumber=positionNumber;
7:this.Departure=departure;
8:this.Destination=destination;
9:}
OK,我們的乘客就可以拿到一張正式的登機牌登機了,haveagoodflight!
1:staticvoidMain(string[]args)
2:{
3:BoardingCheckAttributeboardingCheck=null;
4:object[]customAttributes=typeof(HumanPropertyBase).GetCustomAttributes(true);
5:
6:foreach(varattributeincustomAttributes)
7:{
8:if(attributeisBoardingCheckAttribute)
9:{
10:boardingCheck=attributeasBoardingCheckAttribute;
11:
12:Console.WriteLine(boardingCheck.Name
13:+"'sIDis"
14:+boardingCheck.ID
15:+",he/shewantsto"
16:+boardingCheck.Destination
17:+"from"
18:+boardingCheck.Departure
19:+"bytheplane"
20:+boardingCheck.FlightNumber
21:+",his/herpositionis"
22:+boardingCheck.PositionNumber
23:+".");
24:}
25:}
26:
27:Console.ReadLine();
28:}
第二篇Attribute的理解
本文轉自https://www.cnblogs.com/ppchouyou/archive/2008/03/31/1131593.html
C#中的方括號[](特性、屬性)
約定:
1.”attribute” 和”attributes” 均不翻譯
2.”property” 譯為“屬性”
3.msdn 中的原句不翻譯
4.”program entity” 譯為” 語言元素”
Attributes in C#
介紹
Attributes 是一種新的描述資訊,我們既可以使用attributes 來定義設計期資訊(例如 幫助檔案,文件的URL ),還可以用attributes 定義執行時資訊(例如,使XML 中的元素與類的成員欄位關聯起來)。我們也可以用attributes 來建立一個“自描述”的元件。在這篇指南中我們將明白怎麼建立屬性並將其繫結至各種語言元素上,另外我們怎樣在執行時環境下獲取到attributes 的一些資訊。
定義
MSDN 中做如下定義(ms-help://MS.MSDNQTR.2002APR.1033/csspec/html/vclrfcshARPspec_17_2.htm)
"An attribute is a piece of additional declarative information that is specified for a declaration."
使用預定義Attributes
在c# 中已有一小組預定義的attributes ,在我們學習怎樣建立自定義attributes 前,先來了解下在我們的程式碼中使用那些預定義的attributes.
1 using System; 2 3 public class AnyClass 4 5 { 6 [Obsolete( " Don't use Old method, use New method " , true )] 7 8 static void Old( ) { } 9 10 static void New( ) { } 11 12 public static void Main( ) 13 { 14 Old( ); 15 } 16 }
仔細看下該例項,在該例項中我們用到了”Obsolete”attribute ,它標記了一個不該再被使用的語言元素 ( 譯者注:這裡的元素為方法 ) ,該屬性的第一個引數是string 型別,它解釋為什麼該元素被荒棄,以及我們該使用什麼元素來代替它。實際中,我們可以書寫任何其它文字來代替這段文字。第二個引數是告訴編譯器把依然使用這被標識的元素視為一種錯誤,這就意味著編譯器會因此而產生一個警告。
當我們試圖編譯上面的上面的程式,我們會得到如下錯誤:
AnyClass.Old()' is obsolete: 'Don't use Old method, use New method'
開發自定義Attributes
現在我們即將瞭解怎麼開發自定義的attributes 。這兒有個小小處方,有它我們就可以學會建立自定義的attributes 。
在C# 中,我們的attribute 類都派生於System.Attribute 類( A class that derives from the abstract class System.Attribute, whether directly or indirectly, is an attribute class. The declaration of an attribute class defines a new kind of attribute that can be placed on a declaration ) ,我們就這麼行動吧。
using System; public class HelpAttribute : Attribute { }
不管你是否相信我,就這樣我們就已經建立了一個自定義attribute 。現在就可以用它來裝飾我們的類了,就像我們使用obsolete attribute 一樣。
[Help()] public class AnyClass { }
注意:按慣例我們是用”Attribute“ 作為attribute 類名的字尾,然而,當我們當我們把attribute 繫結到某語言元素時,是不包含“Attribute“ 字尾的。編譯器首先在System.Attribute 的繼承類中查詢該attribute ,如果沒有找到,編譯器會把“Attribute“ 追加到該attribute 的名字後面,然後查詢它。
但是迄今為止,該attribute 沒有任何用處。為了使它有點用處,讓我們在它裡面加點東西吧。
using System; public class HelpAttribute : Attribute { public HelpAttribute(String Descrition_in) { this .description = Description_in; } protected String description; public String Description { get { return this .description; } } } [Help( " this is a do-nothing class " )] public class AnyClass { }
在上面的例子中,我們在attribute 類中添加了一個屬性,在最後一節中,我們將在執行時查詢該屬性。
定義或控制自定義Attribute的用法
AttributeUsage 類是另一預定義類 ( 譯者注:attribute 類本身用這個atrributeSystem.AttributeUsage 來標記 ) ,它將幫助我們控制我們自定義attribute 的用法,這就是,我們能為自定義的attribute 類定義attributes 。
它描述了一個自定義attribute 類能被怎樣使用。
AttributeUsage 提供三個屬性,我們能將它們放置到我們的自定義attribute 類上, 第一個特性是:
ValidOn
通過這個屬性,我們能指定我們的自定義attribute 可以放置在哪些語言元素之上。這組我們能把自定義attribute 類放置其上的語言元素被放在列舉器AttributeTargets 中。我們可以使用bitwise( 譯者注:這個詞不知道怎麼翻譯好,但他的意思是可以這麼用 : [AttributeUsage ( ( AttributeTargets)4 ,AllowMultiple = false ,Inherited = false )], 4 代表就是 “ class ” 元素,其它諸如 1 代表“ assembly ”, 16383 代表“ all ”等等 ) 或者”.” 操做符繫結幾個AttributeTargets 值。 (譯者注:預設值為AttributeTargets.All)
AllowMultiple
該屬性標識我們的自定義attribte 能在同一語言元素上使用多次。 ( 譯者注:該屬性為bool 型別,預設值為false ,意思就是該自定義attribute 在同一語言元素上只能使用一次 )
Inherited
我們可以使用該屬性來控制我們的自定義attribute 類的繼承規則。該屬性標識我們的自定義attribute 是否可以由派生類繼承。( (譯者注:該屬性為bool 型別,預設值為false ,意思是不能繼承)
讓我們來做點實際的東西吧,我們將把AttributeUsageattribute 放置在我們的help attribute 上並在它的幫助下,我們來控制help attribute 的用法。
using System; [AttributeUsage(AttributeTargets.Class, AllowMultiple = false , Inherited = false )] public class HelpAttribute : Attribute { public HelpAttribute(String Description_in) { this .description = Description_in; } protected String description; public String Description { get { return this .description; } } }
首先我們注意AttributeTargets.Class. 它規定這個help attribute 只能放置在語言元素”class” 之上。這就意味著,下面的程式碼將會產生一個錯誤。
AnyClass.cs: Attribute 'Help' is not valid on this declaration type.
It is valid on 'class' declarations only.
現在試著把它繫結到方法。
[Help( " this is a do-nothing class " )] public class AnyClass { [Help( " this is a do-nothing method " )] // error public void AnyMethod() { } }
我們可以使用AttributeTargets.All 來允許Help attribute 可以放置在任何預定義的語言元素上,那些可能的語言元素如下:
- Assembly,
- Module,
- Class,
- Struct,
- Enum,
- Constructor,
- Method,
- Property,
- Field,
- EVEnt,
- Interface,
- Parameter,
- Delegate,
- All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field |EVEnt | Interface | Parameter | Delegate,
- ClassMembers = Class | Struct | Enum | Constructor | Method | Property | Field |EVEnt | Delegate | Interface )
- ~ 現在考慮下AllowMultiple =false. 這就規定該attribute 不能在同一語言元素上放置多次.
-
[Help( " this is a do-nothing class " )] [Help( " it contains a do-nothing method " )] public class AnyClass { [Help( " this is a do-nothing method " )] // error public void AnyMethod() { } }
它產生了一個編譯錯誤:
AnyClass.cs: Duplicate 'Help' attribute
Ok !現在我們該討論下最後那個屬性了,”Inherited”, 指出當把該attribute 放置於一個基類之上,是否派生類也繼承了該attribute 。如果繫結至某個attribute 類的”Inherited” 被設為true, 那麼該attribute 就會被繼承,然而如果繫結至某個attribute 類的”Inherited” 被設為false 或者沒有定義,那麼該attribute 就不會被繼承。
讓我們假設有如下的類關係。
-
[Help( " BaseClass " )] public class Base { } public class Derive : Base { }
我們有四種可能的繫結:
- [AttributeUsage(AttributeTargets.Class, AllowMultiple =false, Inherited =false)]
- [AttributeUsage(AttributeTargets.Class, AllowMultiple =true, Inherited =false)]
- [AttributeUsage(AttributeTargets.Class, AllowMultiple =false, Inherited =true)]
- [AttributeUsage(AttributeTargets.Class, AllowMultiple =true, Inherited =true)]
第一種情況
如果我們查詢(我們將在後面來了解如何在執行時來查詢attributes )派生類中的help attribute ,我們將不可能查詢到因為”Inherited” 被設為了false 。
第二種情況
第二種情況沒有什麼不同,因為其”Inherited” 也被設為了false 。
第三種情況
為了解釋第三種和第四種情況,讓我們為派生類也繫結同一attribute 。
-
[Help( " BaseClass " )] public class Base { } [Help( " DeriveClass " )] public class Derive : Base { }
現在我們查詢相關的help attribute ,我們將僅僅可以得到派生類的attribute ,為什麼這樣是因為help attribute 雖然允許被繼承,但不能多次在同一語言元素上使用,所以基類中的help attribute 被派生類的help attribute 重寫了。
第四種情況
在第四種情況中,當我們查詢派生類的help attribute 時,我們可以得到兩個attributes ,當然是因為help attribute 既允許被繼承,又允許在同一語言元素上多次使用的結果。
注意:AttributeUsageattribute 僅應用在那種是System.Attribute 派生的attriubte 類而且繫結值該attriubte 類的AllowMultiple 和Inherited 均為false 上才是有效的。
可選引數vs.命名引數
可選引數是attribute 類建構函式的引數。它們是強制的,必須在每次在attribute 繫結至某語言元素時提供一個值。而另一方面,命名引數倒是真正的可選引數,不是在attribute 建構函式的引數。
為了更加詳細的解釋,讓我們在Help 類中新增另外的屬性。
-
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false , Inherited = false )] public class HelpAttribute : Attribute { public HelpAttribute(String Description_in) { this .description = Description_in; this .verion = " No Version is defined for this class " ; } protected String description; public String Description { get { return this .description; } } protected String version; public String Version { get { return this .version; } // if we EVE r want our attribute user to set this property, // we must specify set method for it set { this .verion = value; } } } [Help( " This is Class1 " )] public class Class1 { } [Help( " This is Class2 " , Version = " 1.0 " )] public class Class2 { } [Help( " This is Class3 " , Version = " 2.0 " , Description = " This is do-nothing class " )] public class Class3 { }
當我們在Class1 中查詢Help attribute 已經它的屬性,我們將得到:
Help.Description : This is Class1
Help.Version :No Version is defined for this class
因為我們沒有為Version 這個屬性定義任何任何值,所以在建構函式中設定的值被我們查詢出來了。如果沒有定義任何值,那麼就會賦一個該型別的預設值(例如:如果是int 型,預設值就是0 )。
現在,查詢Class2 的結果是:
Help.Description : This is Class2
Help.Version : 1.0
我們不能為了可選引數而使用多個建構函式,應該用已命名引數來代替。我們之所以稱它們為已命名的,是因為當我們在建構函式為它們提供值時,我們必須命名它們。例如,在第二個類中,我們如是定義Help 。
[Help("This is Class2", Version ="1.0")]
在AttributeUsage 例子中, 引數”ValidOn” 是可選引數,而“Inherited“ 和“AllowMultiple“ 是命名引數。
注意:為了在attribute 的建構函式中設定命名引數的值,我們必須為相應的屬性提供一個set 方法否則會引起編譯期錯誤:
'Version' : Named attribute argument can't be a read only property
現在,我們在Class3 中查詢Help attribute 及其屬性會發生什麼呢?結果是跟上面提到的相同的編譯期錯誤。
'Desciption' : Named attribute argument can't be a read only property
現在我們修改下Help 類,為屬性”Description” 加一個set 方法。現在的輸出就是:
Help.Description : This is do-nothing class
Help.Version : 2.0
在屏幕後面究竟發生了什麼呢?首先帶有可選引數的建構函式被呼叫,然後,每個命名引數的set 方法被呼叫,在建構函式中賦給命名引數的值被set 方法所覆寫。
引數型別
一個attribute 類的引數型別被限定在如下型別中:
- bool,
- byte,
- char,
- double,
- float,
- int,
- long,
- short,
- string
- System.Type
- object
- An enum type, provided that it and any types in which it is nested are publicly accessible. A one-dimensional array involving any of the types listed above
Attributes 標記
假設,我們想把Help attribute 繫結至元素assembly 。第一個問題是我們要把Help attribute 放在哪兒才能讓編譯器確定該attribute 是繫結至整個assembly 呢?考慮另一種情況,我們想把attribute 繫結至一個方法的返回型別上,怎樣才能讓編譯器確定我們是把attribute 繫結至方法的返回型別上,而不是整個方法呢?
為了解決諸如此類的含糊問題,我們使用attribute 識別符號,有了它的幫助,我們就可以確切地申明我們把attribute 繫結至哪一個語言元素。
例如:
[assembly: Help("this a do-nothing assembly")]
這個在Help attribute 前的assembly 識別符號確切地告訴編譯器,該attribute 被繫結至整個assembly 。可能的識別符號有:
- assembly
- module
- type
- method
- property
- EVEnt
- field
- param
- return
在執行時查詢Attributes
現在我們明白怎麼建立attribtes 和把它們繫結至語言元素。是時候來學習類的使用者該如何在執行時查詢這資訊。
為了查詢一語言元素上繫結的attributes ,我們必須使用反射。反射有能力在執行時發現型別資訊。
我們可以使用.NET Framework Reflection APIs 通過對整個assembly 元資料的迭代,列舉出assembly 中所有已定義的類,型別,還有方法。
記住那舊的Helpattribute 和AnyClass 類。
-
using System; using System.Reflection; using System.Diagnostics; // attaching Help attribute to entire assembly [assembly : Help( " This Assembly demonstrates custom attributes creation and their run - time query. " )] // our custom attribute class public class HelpAttribute : Attribute { public HelpAttribute(String Description_in) { // // TODO: Add constructor logic here this .description = Description_in; // } protected String description; public String Description { get { return this .deescription; } } } // attaching Help attribute to our AnyClass [HelpString( " This is a do-nothing Class. " )] public class AnyClass { // attaching Help attribute to our AnyMethod [Help( " This is a do-nothing Method. " )] public void AnyMethod() { } // attaching Help attribute to our AnyInt Field [Help( " This is any Integer. " )] public int AnyInt; } class QueryApp { public static void Main() { } }
我們將在接下來的兩節中在我們的Main 方法中加入attribute 查詢程式碼。
查詢程式集的Attributes
在接下來的程式碼中,我們先得到當前的程序名稱,然後用Assembly 類中的LoadForm ()方法載入程式集,再有用GetCustomAttributes ()方法得到被繫結至當前程式集的自定義attributes ,接下來用foreach 語句遍歷所有attributes 並試圖把每個attribute 轉型為Help attribute (即將轉型的物件使用as 關鍵字有一個優點,就是當轉型不合法時,我們將不需擔心會丟擲異常,代之以空值(null )作為結果),接下來的一行就是檢查轉型是否有效,及是不是為空,跟著就顯示Help attribute 的“Description ”屬性。
-
class QueryApp { public static void Main() { HelpAttribute HelpAttr; // Querying Assembly Attributes String assemblyName; Process p = Process.GetCurrentProcess(); assemblyName = p.ProcessName + " .exe " ; Assembly a = Assembly.LoadFrom(assemblyName); foreach (Attribute attr in a.GetCustomAttributes( true )) { HelpAttr = attr as HelpAttribute; if ( null != HelpAttr) { Console.WriteLine( " Description of {0}:\n{1} " , assemblyName,HelpAttr.Description); } } } }
程式輸出如下:
Description of QueryAttribute.exe:
This Assembly demonstrates custom attributes creation and
their run-time query.
Press any key to continue
查詢類、方法、類成員的Attributes
下面的程式碼中,我們惟一不熟悉的就是Main ()方法中的第一行。
Type type =typeof(AnyClass);
它用typeof 操作符得到了一個與我們AnyClass 類相關聯的Type 型物件。剩下的查詢類attributes 程式碼就與上面的例子是相似的,應該不要解釋了吧(我是這麼想的)。
為查詢方法和類成員的attributes, 首先我們得到所有在類中存在的方法和成員,然後我們查詢與它們相關的所有attributes ,這就跟我們查詢類attributes 一樣的方式。
-
class QueryApp { public static void Main() { Type type = typeof (AnyClass); HelpAttribute HelpAttr; // Querying Class Attributes foreach (Attribute attr in type.GetCustomAttributes( true )) { HelpAttr = attr as HelpAttribute; if ( null != HelpAttr) { Console.WriteLine( " Description of AnyClass:\n{0} " , HelpAttr.Description); } } // Querying Class-Method Attributes foreach (MethodInfo method in type.GetMethods()) { foreach (Attribute attr in method.GetCustomAttributes( true )) { HelpAttr = attr as HelpAttribute; if ( null != HelpAttr) { Console.WriteLine( " Description of {0}:\n{1} " , method.Name, HelpAttr.Description); } } } // Querying Class-Field (only public) Attributes foreach (FieldInfo field in type.GetFields()) { foreach (Attribute attr in field.GetCustomAttributes( true )) { HelpAttr = attr as HelpAttribute; if ( null != HelpAttr) { Console.WriteLine( " Description of {0}:\n{1} " , field.Name,HelpAttr.Description); } } } } }
The output of the following program is.
Description of AnyClass:
This is a do-nothing Class.
Description of AnyMethod:
This is a do-nothing Method.
Description of AnyInt:
This is any Integer.
Press any key to continue
-
第三篇 託管
本文轉自https://www.cnblogs.com/yangxx-1990/p/10919612.html
System.Runtime.InteropServices淺見
System.Runtime.InteropServices提供了相應的類或者方法來支援託管/非託管模組間的互相呼叫。
System.Runtime.InteropServices中幾個比較重要的類:
DllImportAttribute:該類提供對非託管動態連結庫進行引用的方法,並告訴我們的編譯器該程式的靜態入口點是非託管的動態連線庫,它的靜態屬性提供了對非託管動態連結庫進行呼叫所必需的資訊,作為最基本的要求,該類應該定義提供呼叫的非託管動態連結庫的名稱。成員詳細資訊
StructLayoutAttribute: 該類使得使用者可以控制類或結構的資料欄位的物理佈局。
publicclassMySystemTime
{
[FieldOffset(0)]publicushortwYear;
[FieldOffset(2)]publicushortwMonth;
[FieldOffset(4)]publicushortwDayOfWeek;
[FieldOffset(6)]publicushortwDay;
[FieldOffset(8)]publicushortwHour;
[FieldOffset(10)]publicushortwMinute;
[FieldOffset(12)]publicushortwSecond;
[FieldOffset(14)]publicushorwMilliseconds;
}
MarshalAsAttribute :指示如何在託管程式碼和非託管程式碼之間封送資料。下面是MSDN給出的示例程式碼:
[C#]//Appliedtoaparameter.
publicvoidM1([MarshalAs(UnmanagedType.LPWStr)]Stringmsg);
//Appliedtoafieldwithinaclass.
classMsgText{
[MarshalAs(UnmanagedType.LPWStr)]PublicStringmsg;
}
//Appliedtoareturnvalue.
[return:MarshalAs(UnmanagedType.LPWStr)]
publicStringGetMessage();
一個將三個類綜合運用的例項:呼叫kernel32.dll中的非託管方法"GetSystemTime"將系統時間返回給定製的類MySystemTime並執行輸出.
usingSystem;usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.Runtime.InteropServices;
namespaceDllImportTest
{
///<summary>
///定義一個用於接收非託管API方法返回值的類
///StructLayout定義了該類的各個成員在物理上的排列方式
///</summary>
[StructLayout(LayoutKind.Explicit,Size=16,CharSet=CharSet.Ansi)]
publicclassMySystemTime
{
[FieldOffset(0)]
publicushortwYear;
[FieldOffset(2)]
publicushortwMonth;
[FieldOffset(4)]
publicushortwDayOfWeek;
[FieldOffset(6)]
publicushortwDay;
[FieldOffset(8)]
publicushortwHour;
[FieldOffset(10)]
publicushortwMinute;
[FieldOffset(12)]
publicushortwSecond;
[FieldOffset(14)]
publicushortwMilliseconds;
}
///<summary>
///用LibWrapper的靜態方法來呼叫非託管API方法"GetSystemTime"
///</summary>
classLibWrapper
{
[DllImport("kernel32.dll",EntryPoint="GetSystemTime")]
//如果定義的方法名稱與要進行封裝的非託管API方法不同則需要在DLLImport中指定入口點.
publicstaticexternvoidgettime([MarshalAs(UnmanagedType.LPStruct)]MySystemTimest);
}
classTestApplication
{
publicstaticvoidMain()
{
try
{
MySystemTimesysTime=newMySystemTime();
//LibWrapper.GetSystemTime(sysTime);
LibWrapper.gettime(sysTime);
Console.WriteLine("TheSystemtimeis{0}/{1}/{2}{3}:{4}:{5}",sysTime.wDay,
sysTime.wMonth,sysTime.wYear,sysTime.wHour,sysTime.wMinute,sysTime.wSecond);
}
catch(TypeLoadExceptione)
{
Console.WriteLine("TypeLoadException:"+e.Message);
}
catch(Exceptione)
{
Console.WriteLine("Exception:"+e.Message);
}
}
}
}