屬性提供以宣告方式將資訊與程式代碼產生關聯的方式。 它們也可以提供可重複使用的元素,適用於各種目標。 請考慮ObsoleteAttribute。 它可以套用至類別、結構、方法、建構函式等等。 它會 宣告 元素已過時。 接著,C# 編譯程式會尋找此屬性,並執行一些回應動作。
在本教學課程中,您將瞭解如何將屬性新增至程序代碼、如何建立和使用您自己的屬性,以及如何使用 .NET 內建的某些屬性。
先決條件
您必須設定電腦以執行 .NET。 您可以在 .NET 下載 頁面上找到安裝指示。 您可以在 Windows、Ubuntu Linux、macOS 或 Docker 容器中執行此應用程式。 您必須安裝您慣用的程式碼編輯器。 下列描述使用 Visual StudioCode,這是開放原始碼的跨平台編輯器。 不過,您可以使用任何您熟悉的工具。
建立應用程式
現在您已安裝所有工具,請建立新的 .NET 控制台應用程式。 若要使用指令生成器,請在您最喜愛的終端中執行下列命令:
dotnet new console
此命令會建立簡易的 .NET 專案檔案。 您可以執行 dotnet restore
來還原編譯此專案所需的相依性。
您不必執行 dotnet restore
,因為其會由需要進行還原的所有命令隱含執行,例如 dotnet new
、dotnet build
、dotnet run
、dotnet test
、dotnet publish
和 dotnet pack
。 若要停用隱含還原,請使用 --no-restore
選項。
dotnet restore
命令在適合進行明確還原的特定案例中仍可派上用場,例如 Azure DevOps Services 中的持續整合組建,或在需要明確控制何時進行還原的組建系統中。
若要了解如何管理 NuGet 套件源,請參閱 dotnet restore
文件。
若要執行程式,請使用 dotnet run
。 您應該會看到主控台的 「Hello, World」 輸出。
將屬性新增至程序代碼
在 C# 中,屬性是繼承自基類的 Attribute
類別。 繼承自 Attribute
的任何類別都可以做為其他程式代碼片段的「標記」。 例如,有一 ObsoleteAttribute
個名為的屬性。 這個屬性表示程式代碼已經過時,而且不應該再使用。 例如,您可以使用方括號將這個屬性放在類別上。
[Obsolete]
public class MyClass
{
}
雖然這個類別叫做 ObsoleteAttribute
,但在代碼中只需要使用 [Obsolete]
。 大部分 C# 程式代碼都遵循此慣例。 如果您選擇,可以使用完整名稱 [ObsoleteAttribute]
。
將類別標示為過時時,最好提供一些資訊,說明 其過時的原因 ,以及/或要改用 的內容 。 您將字串參數包含在過時屬性中,以提供此說明。
[Obsolete("ThisClass is obsolete. Use ThisClass2 instead.")]
public class ThisClass
{
}
字串會被作為參數傳遞到 ObsoleteAttribute
構造函數中,就像您正在撰寫 var attr = new ObsoleteAttribute("some string")
一樣。
屬性建構函式的參數僅限於簡單類型/常值: bool, int, double, string, Type, enums, etc
和這些類型的陣列。
您無法使用表示式或變數。 您可以自由使用位置或具名參數。
建立您自己的屬性
您可以定義繼承自 Attribute
基類的新類別來建立屬性。
public class MySpecialAttribute : Attribute
{
}
使用上述程式代碼,您可以使用 [MySpecial]
(或 [MySpecialAttribute]
) 作為程式代碼基底中其他地方的屬性。
[MySpecial]
public class SomeOtherClass
{
}
.NET 基類庫中的屬性,例如 ObsoleteAttribute
觸發編譯程式內的特定行為。 不過,您建立的任何屬性都只能做為元數據,而且不會執行屬性類別內的任何程序代碼。 您必須對程式代碼中其他地方的元數據採取行動。
這裡有一個「陷阱」要注意。 如先前所述,使用屬性時,只能將特定類型當做自變數傳遞。 不過,建立屬性類型時,C# 編譯程式不會阻止您建立這些參數。 在下列範例中,您已使用正確編譯的建構函式建立屬性。
public class GotchaAttribute : Attribute
{
public GotchaAttribute(Foo myClass, string str)
{
}
}
不過,您無法搭配屬性語法使用此建構函式。
[Gotcha(new Foo(), "test")] // does not compile
public class AttributeFail
{
}
上述程式代碼會導致編譯程序錯誤,例如 Attribute constructor parameter 'myClass' has type 'Foo', which is not a valid attribute parameter type
如何限制屬性使用方式
屬性可用於下列「目標」。 在前面的範例中,它們是用於類別,但也可以用於:
- 集會
- 班級
- 建構函式
- 代表
- 列舉
- 事件
- 領域
- 泛型參數
- 介面
- 方法
- 模組
- 參數
- 房產
- 返回值
- 結構體
當您建立屬性類別時,根據預設,C# 可讓您在任何可能的屬性目標上使用該屬性。 如果您想要將屬性限制為特定目標,您可以使用 屬性類別上的 來執行此 AttributeUsageAttribute
動作。 沒錯,一個屬性在另一個屬性上!
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class MyAttributeForClassAndStructOnly : Attribute
{
}
如果您嘗試將上述屬性放在不是類別或結構的專案上,您會收到編譯程序錯誤,例如 Attribute 'MyAttributeForClassAndStructOnly' is not valid on this declaration type. It is only valid on 'class, struct' declarations
public class Foo
{
// if the below attribute was uncommented, it would cause a compiler error
// [MyAttributeForClassAndStructOnly]
public Foo()
{ }
}
如何使用附加至程式碼元素的屬性
屬性會做為元數據。 沒有一些外力,它們實際上不會有任何動作。
若要尋找並處理屬性,需要使用反射。 反映可讓您以 C# 撰寫程式代碼,以檢查其他程式碼。 例如,您可以使用 Reflection 來取得類別的相關信息(在程式碼的前端新增 using System.Reflection;
):
TypeInfo typeInfo = typeof(MyClass).GetTypeInfo();
Console.WriteLine("The assembly qualified name of MyClass is " + typeInfo.AssemblyQualifiedName);
這樣會列印如下的內容: The assembly qualified name of MyClass is ConsoleApplication.MyClass, attributes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
一旦您擁有 TypeInfo
物件(或 MemberInfo
、 FieldInfo
或其他 物件),就可以使用 GetCustomAttributes
方法。 這個方法會傳回 物件的集合 Attribute
。 您也可以使用 GetCustomAttribute
並指定屬性類型。
以下是一個使用GetCustomAttributes
於MemberInfo
實例的範例,針對MyClass
(我們稍早看到它有[Obsolete]
屬性)。
var attrs = typeInfo.GetCustomAttributes();
foreach(var attr in attrs)
Console.WriteLine("Attribute on MyClass: " + attr.GetType().Name);
它會列印到主控台: Attribute on MyClass: ObsoleteAttribute
。 請嘗試將其他屬性新增至 MyClass
。
請務必注意,這些 Attribute
物件會延遲具現化。 也就是說,除非您使用 GetCustomAttribute
或 GetCustomAttributes
,否則不會具現化它們。 每次也會具現化它們。 連續呼叫兩次 GetCustomAttributes
會傳回兩個不同的 ObsoleteAttribute
實例。
運行時間中的常見屬性
許多工具和架構都會使用屬性。 NUnit 會使用 NUnit 測試執行器所使用的 屬性,例如 [Test]
和 [TestFixture]
。 ASP.NET MVC 會使用 之類的 [Authorize]
屬性,並提供動作篩選架構,對MVC動作執行跨領域考慮。
PostSharp 會使用 屬性語法來允許 C# 中的面向面向程序設計。
以下是 .NET Core 基類連結庫內建的一些值得注意的屬性:
-
[Obsolete]
。 這一個用於上述範例中,而且它位於 命名空間中System
。 提供描述正在變更的程式碼基底的文件非常有用。 訊息可以以字串形式提供,另一個布爾參數可用來將編譯器警告升級為編譯器錯誤。 -
[Conditional]
。 這個屬性位於 命名空間中System.Diagnostics
。 這個屬性可以套用至方法(或屬性類別)。 您必須將字串傳遞至建構函式。 如果該字串不符合#define
指示詞,則 C# 編譯程式會移除該方法的任何呼叫(但不是方法本身)。 通常您會使用這項技術進行偵錯(診斷)。 -
[CallerMemberName]
。 這個屬性可用於參數,並存在於命名空間中System.Runtime.CompilerServices
。CallerMemberName
是用來插入呼叫另一個方法之方法名稱的屬性。 在各種UI架構中實作INotifyPropertyChanged時,這是消除「魔術字串」的方法。 例如:
public class MyUIClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string propertyName = default!)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string? _name;
public string? Name
{
get { return _name;}
set
{
if (value != _name)
{
_name = value;
RaisePropertyChanged(); // notice that "Name" is not needed here explicitly
}
}
}
}
在上述程式代碼中,您不需要有常值 "Name"
字串。 使用 CallerMemberName
可防止錯字相關的 Bug,也可讓您更順暢地重構/重新命名。 屬性為 C# 帶來宣告式功能,但它們是程式代碼的元數據形式,而且不會自行採取行動。