特性(attribute)是被指定給某壹聲明的壹則附加的聲明性信息。
在C#中,有壹個小的預定義特性集合。在學習如何建立我們自己的定制特性(custom attributes)之前,我們先來看看在我們的代碼中如何使用預定義特性。
using System;
public class AnyClass
{
[Obsolete(”Don’t use Old method, use New method”, true)]
static void Old( ) { }
static void New( ) { }
public static void Main( )
{
Old( );
}
}
我們先來看壹下上面這個例子,在這個例子中我們使用了Obsolete特性,它標記了壹個不應該再被使用的程序實體。第壹個參數是壹個字符串,它解釋了為什麽該實體是過時的以及應該用什麽實體來代替它。實際上,妳可以在這裏寫任何文本。第二個參數告訴編譯器應該把使用這個過時的程序實體當作壹種錯誤。它的默認值是false,也就是說編譯器對此會產生壹個警告。
當我們嘗試編譯上面這段程序的時候,我們將會得到壹個錯誤:
AnyClass.Old()’ is obsolete: ‘Don’t use Old method, use New method’
開發定制特性(custom attributes)
現在讓我們來看看如何開發我們自己的特性。
首先我們要從System.Attribute派生出我們自己的特性類(壹個從System.Attribute抽象類繼承而來的類,不管是直接還是間接繼承,都會成為壹個特性類。特性類的聲明定義了壹種可以被放置在聲明之上新的特性)。
using System;
public class HelpAttribute : Attribute
{
}
不管妳是否相信,我們已經建立了壹個定制特性,現在我們可以用它來裝飾現有的類就好像上面我們使用Obsolete attribute壹樣。
[Help()]
public class AnyClass
{
}
註意:對壹個特性類名使用Attribute後綴是壹個慣例。然而,當我們把特性添加到壹個程序實體,是否包括Attribute後綴是我們的自由。編譯器會首先在System.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
{
}
在上面的例子中,我們給HelpAttribute特性類添加了壹個屬性並且在後續的部分中我們會在運行時環境中查尋它。
定義或控制特性的使用
AttributeUsage類是另外壹個預定義特性類,它幫助我們控制我們自己的定制特性的使用。它描述了壹個定制特性如和被使用。
AttributeUsage有三個屬性,我們可以把它放置在定制屬性前面。第壹個屬性是:
ValidOn
通過這個屬性,我們能夠定義定制特性應該在何種程序實體前放置。壹個屬性可以被放置的所有程序實體在AttributeTargets enumerator中列出。通過OR操作我們可以把若幹個AttributeTargets值組合起來。
AllowMultiple
這個屬性標記了我們的定制特性能否被重復放置在同壹個程序實體前多次。
Inherited
我們可以使用這個屬性來控制定制特性的繼承規則。它標記了我們的特性能否被繼承。
下面讓我們來做壹些實際的東西。我們將會在剛才的Help特性前放置AttributeUsage特性以期待在它的幫助下控制Help特性的使用。
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特性只能被放在class的前面。這也就意味著下面的代碼將會產生錯誤:
[Help(”this is a do-nothing class”)]
public class AnyClass
{
[Help(”this is a do-nothing method”)] //error
public void AnyMethod()
{
}
}
編譯器報告錯誤如下:
AnyClass.cs: Attribute ‘Help’ is not valid on this declaration type.
It is valid on ‘class’ declarations only.
我們可以使用AttributeTargets.All來允許Help特性被放置在任何程序實體前。可能的值是:
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。它規定了特性不能被重復放置多次。
[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, 表明當特性被放置在壹個基類上時,它能否被派生類所繼承。
[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 ]
第壹種情況:
如果我們查詢(Query)(稍後我們會看到如何在運行期查詢壹個類的特性)Derive類,我們將會發現Help特性並不存在,因為inherited屬性被設置為false。
第二種情況:
和第壹種情況相同,因為inherited也被設置為false。
第三種情況:
為了解釋第三種和第四種情況,我們先來給派生類添加點代碼:
[Help(”BaseClass”)]
public class Base
{
}
[Help(”DeriveClass”)]
public class Derive : Base
{
}
現在我們來查詢壹下Help特性,我們只能得到派生類的屬性,因為inherited被設置為true,但是AllowMultiple卻被設置為false。因此基類的Help特性被派生類Help特性覆蓋了。
第四種情況:
在這裏,我們將會發現派生類既有基類的Help特性,也有自己的Help特性,因為AllowMultiple被設置為true。
定義或控制特性的使用
AttributeUsage類是另外壹個預定義特性類,它幫助我們控制我們自己的定制特性的使用。它描述了壹個定制特性如和被使用。
AttributeUsage有三個屬性,我們可以把它放置在定制屬性前面。第壹個屬性是:
ValidOn
通過這個屬性,我們能夠定義定制特性應該在何種程序實體前放置。壹個屬性可以被放置的所有程序實體在AttributeTargets enumerator中列出。通過OR操作我們可以把若幹個AttributeTargets值組合起來。
AllowMultiple
這個屬性標記了我們的定制特性能否被重復放置在同壹個程序實體前多次。
Inherited
分類: Unix
通過串口關閉Linux服務器
壹個真正的服務器壹般往往不需要連接鍵盤、顯示器或鼠標等外設。而僅僅需要壹個性能穩定的操作系統如Linux和壹個網卡。諸如鼠標、鍵盤等外部設備則往往不必要連接。在運行Linux的機器上,通過網絡進行遠程管理是最好和最方便的解決方法。
在買服務器時應該確保在開機BIOS中有設定不需要檢測鍵盤的選項。目前市場上大多數主板廠商的BIOS版本都有這樣的選項。但是仍然有壹些主板認為自己的的服務器將只運行windows,因此壹定會在啟動時需要壹個鍵盤,因此不推薦使用這種機器作為服務器使用。
沒有鍵盤和監視器的計算機非常適合於作為防火墻、web服務器、NFS服務器、郵件服務器的、ADSL網關、DNS服務器的概念等等。而且該機器不壹定是功能非常強大的機器。即使壹個老的Pentimu100機器,只要擁有足夠的內存就可以同時服務數百個客戶服務請求。
服務器不連接鍵盤、鼠標和顯示器的好處是非常方便,因為服務器將只是壹個簡單的主機箱,可以放在任何適合的地方。但有時候妳可能希望能有壹個按鍵來實現安全的關閉服務器而無需首先遠程登錄服務器運行shutdown命令來實現關閉機器。也可能妳現在就在服務器機房,而最近的工作站離妳有100多米遠;或者妳希望某個沒有root密碼的普通用戶關閉服務器。當然直接關閉電源並不是壹個好的想法,因為這樣可能導致丟失數據並且在下次啟動時需要很長時間的文件系統檢測。
解決方法是按照本文的方法安裝壹個很小的、便宜的串口設備實現安全關機。
設備
我們將制作壹個小小的、由兩個LED和壹個按鍵式開關組成的設備。其中綠色的LED亮表示當前按鍵式開關是處於激活狀態,緩慢閃爍的黃色LED燈表示服務器正在啟動或者仍然處於關閉狀態。為了實現安全關閉服務器需要持續按下開關3秒鐘。
如果妳按了按鍵開關,亮著的綠色的LED將熄滅,表示正在關機,這時候命令”/sbin/shutdown -t2 -h now”被執行。在服務器關閉過程中黃色的LED將連續閃爍以進行指示。當黃色LED不再閃爍時,妳仍然需要等待10秒鐘才能關閉服務器電源。當所有的進程被殺掉,磁盤被卸載以後黃色LED將不再閃爍。若妳的服務器支持自動關機則妳不需要考慮10秒的等待時間,因為服務器會自動關閉電源。
串口
關於串口令人感興趣的是我們可以通過ioctl系統調用來控制其中的幾個管腳。這意味著妳不需要修改任何內核代碼就可以控制連接到串口的LED或這讀取按鍵式開關的狀態。串口有足夠的電壓來驅動兩個低電壓LED。我們將使用RTS和DTR針來連接LED,並且”Carrier Detect line”被用來讀取按鍵開關的狀態。
串口母頭連接器的管腳定義:
9 PIN-connector 25 PIN-connector Name Dir Description
1 8 CD input Carrier Detect
2 3 RXD input Receive Data
3 2 TXD output Transmit Data
4 20 DTR output Data Terminal Ready
5 7 GND — System Ground
6 6 DSR input Data Set Ready
7 4 RTS output Request to Send
8 5 CTS input Clear to Send
9 22 RI input Ring Indicator
電路
用戶需要下面元器件來制作這個串口關機設備:
1 綠色低電壓LED (壹個普通LED同樣可以工作,但是亮燈過低)
1 黃色低電壓LED
2 1.5K歐姆的電阻
1 6.8K歐姆的電阻
1 小按鍵開關
1 串口母頭連接器
這些元器件按照如下的電路圖連接在壹起。串口電壓範圍是+/- 10伏特。1.5K電阻適合於低電壓的LED,但是普通LED同樣適合。不推薦修改電阻器電壓和使用外部電源,因為串口控制芯片是主板的壹部分,壹旦妳燒了其中的芯片,妳只有修改整個主板。
上圖是實際的電路板樣品圖。最後的設備如文章開始圖片所示。
軟件
軟件部分是壹個小的守護進程。它可以通過/etc/rc.d/rc3.d/的腳本被啟動。它通過使用ioctl(fd, TIOCMSET,…)和ioctl(fd, TIOCMGET, …)函數來讀取和寫串口。妳可以從這裏下載程序。軟件中包含壹個Makefile和安裝指示。
編寫Linux 桌面腳本
為 Linux 桌面開發應用程序通常需要壹些類型的圖形用戶界面(Graphical User Interface,GUI)框架作為構建基礎。選項包括針對 GNOME 桌面的 GTK+,和針對 K 桌面環境(K Desktop Environment,KDE)的 Qt。這兩個平臺提供了開發人員構建 GUI 應用程序所需的壹切,包括庫和布局工具以便創建用戶看到的窗口。本文向您展示如何基於 screenlet 小部件工具包構建桌面生產率應用程序。
壹些現有的應用程序將歸為桌面生產率類別,包括 GNOME Do 和 Tomboy。這些應用程序通常允許用戶直接從桌面與它們進行交互,方法為通過特定的鍵組合或從另壹個應用程序(如 Mozilla Firefox)進行拖放。Tomboy 用作桌面筆記工具,支持從其他窗口拖放文本。
Screenlet 入門
您需要安裝壹些程序以便開始開發 screenlet。首先,使用 Ubuntu 軟件中心或命令行安裝 screenlets 包。在 Ubuntu 軟件中心內,在 Search框中鍵入 screenlets。您應該看到主要程序包和文檔的獨立安裝包兩個選項。
Python 和 Ubuntu您可使用 Python 對 screenlet 編程。Ubuntu 10.04 的基本安裝已包含了 Python 2.6,因為許多實用程序都依賴它。您可能需要其他庫,具體取決於您的應用程序要求。對於本文,我在 Ubuntu 10.04 上安裝並測試了壹切。下壹步,從 screenlets.org 站點下載測試 screenlet 源代碼。測試 screenlet 位於 src/share/screenlets/Test 文件夾並使用 Cairo 和 GTK,這些也是需要您安裝的。測試程序的整個源代碼位於 TestScreenlet.py 文件中。在您最喜愛的編輯器中打開此文件來查看 screenlet 的基礎結構。
Python 高度面向對象,因此使用 class關鍵字來定義對象。在本示例中,該類被命名為 TestScreenlet並具有壹些已定義的方法。在 TestScreenlet.py 中,請註意下面代碼中的第 42 行:
def __init__(self, **keyword_args):Python 使用前後雙下劃線(__)符號,通過預定義行為識別系統函數。在本例中,__init__函數針對類的構造函數的所有的意圖和目的,且包含將要在創建對象的新實例時執行的任何數量的初始化步驟。按照慣例,每個類方法的第壹個參數都是對該類的當前實例的引用,並命名為 self。通過此行為,可以方便地使用 self來引用它所在的實例的方法和屬性:
self.theme_name = “default”Screenlet 框架定義了壹些命名慣例和標準,在 screenlets.org 中的開發人員頁面有概述。還有 screenlet 包的源代碼以及應用程序編程接口(Application Programming Interface,API)文檔的鏈接。查看代碼還使您可以深入了解每壹個函數可以通過調用參數做什麽以及返回什麽。
編寫簡單的 screenlet
Screenlet 的基本組成部分包括圖標文件、源代碼文件和主題文件夾。主題文件夾包含不同主題的附加文件夾。您將在 screenlets.org 上發現樣例模板和幫助您入門所需的文件和文件夾。
對於第壹個示例來說,使用已提供的模板來創建基本的 “Hello World” 應用程序。此基本應用程序的代碼如清單 1 所示。
清單 1. Hello World screenlet 的 Python 代碼
#!/usr/bin/env python
import screenlets
class HelloWorldScreenlet(screenlets.Screenlet):
__name__ = ‘HelloWorld’
__version__ = ‘0.1’
__author__ = ‘John Doe’
__desc__ = ‘Simple Hello World Screenlet’
def __init__(self, **kwargs):
# Customize the width and height.
screenlets.Screenlet.__init__(self, width=180, height=50, **kwargs)
def on_draw(self, ctx):
# Change the color to white and fill the screenlet.
ctx.set_source_rgb(255, 255, 255)
self.draw_rectangle(ctx, 0, 0, self.width, self.height)
# Change the color to black and write the message.
ctx.set_source_rgb(0, 0, 0)
text = ‘Hello World!’
self.draw_text(ctx, text, 10, 10, “Sans 9” , 20, self.width)
if __name__ == “__main__”:
import screenlets.session
screenlets.session.create_session(HelloWorldScreenlet)每壹個應用程序都必須導入 screenlet 框架並創建新的會話。還有壹些其他的最低要求,包括任何初始化步驟以及基本繪圖函數,以便在屏幕上呈現小部件。TestScreenlet.py 示例具有用來初始化對象的 __init__方法。在本例中,您將看到壹行,包含對 screenlet 的 __init__方法的調用,它設置要為此應用程序創建的窗口的初始寬度和高度。
此應用程序惟壹需要的其他函數是 on_draw方法。此例程將框的背景顏色設置為白色,並使用先前定義的維度繪制矩形。它將文本顏色設置為黑色,將源文本設置為 “Hello World!”,然後再繪制該文本。圖 1 顯示了在運行此 screenlet 時您應該看到什麽。在文本的後面部分,您在這些簡單塊上創建更有用的應用程序時,此基本結構壹直存在。
圖 1. 基本 screenlet 結構
在更復雜的 screenlet 中重用代碼
壹個有關編寫 screenlet 的好處就是能夠重用來自其他應用程序的代碼。通過基於 Python 語言的廣泛開源項目,代碼重用開拓了無限的可能性。雖然每壹個 screenlet 都有相同的基本結構,但是定義了的更多方法來處理不同的行為。清單 2 顯示了名為TimeTrackerScreenlet的樣例應用程序。
清單 2. Time Tracker screenlet 的 Python 代碼
#!/usr/bin/env python
import screenlets
import cairo
import datetime
class TimeTrackerScreenlet(screenlets.Screenlet):
__name__ = ‘TimeTrackerScreenlet’
__version__ = ‘0.1’
__author__ = ‘John Doe’
__desc__ = ‘A basic time tracker screenlet.’
theme_dir = ‘themes/default’
image = ‘start.png’
def __init__(self, **keyword_args):
screenlets.Screenlet.__init__(self, width=250, height=50, **keyword_args)
self.add_default_menuitems()
self.y = 25
self.theme_name = ‘default’
self.on = False
self.started = None
def on_draw(self, ctx):
self.draw_scaled_image(ctx, 0, 0, self.theme_dir + ‘/’ +
self.image, self.width, self.height)
def on_mouse_down(self, event):
if self.on:
self.started = datetime.datetime.now()
self.image = ‘stop.png’
self.on = False
else:
if self.started:
length = datetime.datetime.now() – self.started
screenlets.show_message(None, ‘%s seconds’ %
length.seconds, ‘Time’)
self.started = None
self.image = ‘start.png’
self.on = True
def on_draw_shape(self, ctx):
self.on_draw(ctx)
ctx.rectangle(0, 0, self.width, self.height)
ctx.fill()
if __name__ == “__main__”:
import screenlets.session
screenlets.session.create_session(TimeTrackerScreenlet)此示例引入了幾個在您開始構建任何有用的程序以前需要了解的概念。所有的 screenlet 應用程序都具有響應特定用戶操作或事件(如鼠標單擊或拖放操作)的能力。在此示例中,鼠標按下事件用作更改圖標狀態的觸發器。在 screenlet 運行時,顯示 start.png 圖像。單擊該圖像將其變更為 stop.png 圖像並在 self.started中記錄開始時間。單擊停止圖像將該圖像變更回 start.png 並顯示從單擊第壹個圖像開始所經過的時間量。