泛型属性
在 C#11 中,开发人员可以声明一种基类为 System.Attribute
的泛型类。这种特性提供了一种更方便的语法,用于需要一个 System.Type
参数的属性。之前,您需要创建一个以 Type
作为构造函数参数的属性:
// Before C# 11:
public class TypeAttribute : Attribute
{
public TypeAttribute(Type t) => ParamType = t;
public Type ParamType { get; }
}
然后使用 typeof
运算符应用属性:
[TypeAttribute(typeof(string))]
public string Method() => default;
使用这个新特性,您可以创建一个泛型属性:
// C# 11 feature:
public class GenericAttribute<T> : Attribute { }
然后,指定要使用属性的类型参数:
[GenericAttribute<string>()]
public string Method() => default;
应用属性时,必须提供所有类型参数。换句话说,泛型类型必须完全构造。在上面的示例中,空括号 ()
可以省略,因为属性没有任何参数。
public class GenericType<T>
{
[GenericAttribute<T>()] // Not allowed! generic attributes must be fully constructed types.
public string Method() => default;
}
类型参数必须满足 typeof
运算符的相同限制。不允许使用需要元数据注释的类型。例如,以下类型不能作为类型参数:
dynamic
string?
(或任何可空引用类型)(int X, int Y)
(或使用 C# 元组语法的任何其他元组类型)
这些类型在元数据中没有直接表示。它们包括描述类型的注释。在所有情况下,您都可以使用底层类型:
object
代替dynamic
string
代替string?
ValueTuple<int, int>
代替(int X, int Y)
以下是使用泛型属性的示例:
using System;
[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute<T> : Attribute
{
public MyAttribute()
{
Value = default(T);
}
public T Value { get; }
}
public class MyClass
{
[MyAttribute<string>()]
public void MyMethod1()
{
Console.WriteLine("MyMethod1");
}
[MyAttribute<int>()]
public void MyMethod2()
{
Console.WriteLine("MyMethod2");
}
}
class Program
{
static void Main(string[] args)
{
var methods = typeof(MyClass).GetMethods();
foreach (var method in methods)
{
var attributes = method.GetCustomAttributes(false);
foreach (var attribute in attributes)
{
if (attribute is MyAttribute<string> myStringAttribute)
{
Console.WriteLine($"Method {method.Name} has MyAttribute<string> with value {myStringAttribute.Value}");
}
else if (attribute is MyAttribute<int> myIntAttribute)
{
Console.WriteLine($"Method {method.Name} has MyAttribute<int> with value {myIntAttribute.Value}");
}
}
}
}
}
接下来,让我们更深入地了解如何使用泛型属性来声明具有不同类型参数的属性。假设我们有一个 Person
类,它有一个属性 Age
,我们希望使用泛型属性来限制其属性值的类型。
public class Person
{
[TypeCheck(typeof(int))]
public int Age { get; set; }
}
[AttributeUsage(AttributeTargets.Property)]
public class TypeCheckAttribute<T> : Attribute
{
public TypeCheckAttribute()
{
TypeToCheck = typeof(T);
}
public Type TypeToCheck { get; }
}
上述代码中,我们声明了一个 TypeCheckAttribute
泛型属性,它使用 typeof
运算符来获取属性值的类型。然后,我们将此属性应用于 Person
类的 Age
属性。在此示例中,Age
属性必须是 int
类型。
接下来,让我们使用另一个示例来说明泛型属性的用途。假设我们有一个类 Student
,它有两个属性:Name
和 Age
。我们希望使用泛型属性来限制 Name
属性的值必须是字符串类型,并将 Age
属性的值限制为整数类型。以下是代码示例:
public class Student
{
[TypeCheck(typeof(string))]
public string Name { get; set; }
[TypeCheck(typeof(int))]
public int Age { get; set; }
}
[AttributeUsage(AttributeTargets.Property)]
public class TypeCheckAttribute<T> : Attribute
{
public TypeCheckAttribute()
{
TypeToCheck = typeof(T);
}
public Type TypeToCheck { get; }
}
在上述代码中,我们声明了一个泛型属性 TypeCheckAttribute
,它使用 typeof
运算符来获取属性值的类型。然后,我们将此属性应用于 Student
类的 Name
和 Age
属性。在此示例中,Name
属性必须是 string
类型,Age
属性必须是 int
类型。
最后,让我们看一下如何在运行时使用泛型属性。在以下示例中,我们使用反射获取 Student
类型的属性,并检查它们是否带有 TypeCheckAttribute
泛型属性。如果找到此属性,则检查其 TypeToCheck
属性,以确保属性值的类型符合要求。
class Program
{
static void Main(string[] args)
{
var student = new Student { Name = "John Doe", Age = 25 };
var properties = typeof(Student).GetProperties();
foreach (var property in properties)
{
var attributes = property.GetCustomAttributes(false);
foreach (var attribute in attributes)
{
if (attribute is TypeCheckAttribute<int> && property.PropertyType != typeof(int))
{
Console.WriteLine($"{property.Name} must be of type {typeof(int)}");
}
else if (attribute is TypeCheckAttribute<string> && property.PropertyType != typeof(string))
{
Console.WriteLine($"{property.Name} must be of type {typeof(string)}");
}
}
}
}
}
在上述示例中,我们创建了一个 Student
实例,然后使用反射获取其属性。我们遍历属性,并检查它们是否带有 TypeCheckAttribute
泛型属性。如果找到此属性,则我们检查其 TypeToCheck
属性,以确保属性值的类型符合要求。
以上就是泛型属性的介绍和示例。泛型属性是一种强大的特性,它可以帮助开发人员更方便地创建属性,并限制属性值的类型。使用泛型属性,开发人员可以轻松地创建具有不同类型参数的属性,并在运行时对其进行检查。