C#:特性

特性介绍


使用特性,可以有效地将元数据或声明性信息与代码(程序集、类型、方法、属性等)相关联。 将特性与程序实体相关联后,可以在运行时使用反射这项技术查询特性。 有关详细信息,请参阅反射 (C#)

特性具有以下属性:

  • 特性向程序添加元数据。 元数据是程序中定义的类型的相关信息。 所有 .NET 程序集都包含一组指定的元数据,用于描述程序集中定义的类型和类型成员。 可以添加自定义特性来指定所需的其他任何信息。
  • 可以将一个或多个特性应用于整个程序集、模块或较小的程序元素(如类和属性)。
  • 特性可以像方法和属性一样接受自变量。
  • 程序可使用反射来检查自己的元数据或其他程序中的元数据。

特性的常见用途:

  • 在 Web 服务中使用 WebMethod 特性标记方法,以指明方法应可通过 SOAP 协议进行调用。 有关详细信息,请参阅 WebMethodAttribute
  • 描述在与本机代码互操作时如何封送方法参数。 有关详细信息,请参阅 MarshalAsAttribute
  • 描述类、方法和接口的 COM 属性。
  • 使用 DllImportAttribute 类调用非托管代码。
  • 从标题、版本、说明或商标方面描述程序集。
  • 描述要序列化并暂留类的哪些成员。
  • 描述如何为了执行 XML 序列化在类成员和 XML 节点之间进行映射。
  • 描述的方法的安全要求。
  • 指定用于强制实施安全规范的特征。
  • 通过实时 (JIT) 编译器控制优化,这样代码就一直都易于调试。
  • 获取方法调用方的相关信息。

使用示例:


可以将特性附加到几乎任何声明中,尽管特定特性可能会限制可有效附加到的声明的类型。 在 C# 中,通过用方括号 ([]) 将特性名称括起来,并置于应用该特性的实体的声明上方以指定特性。

在此示例中,SerializableAttribute 特性用于将具体特征应用于类:

// 不带参数的特性
[Serializable]
// 带参数的特性
[DllImport("user32.dll")]
public class SampleClass
{
    // Objects of this type can be serialized.
}

按照约定,所有特性名称均以“Attribute”一词结尾,以便与 .NET 库中的其他项区分开来。 不过,在代码中使用特性时,无需指定特性后缀。 例如,[Serializable]实际是[SerializableAttribute],SerializableAttribute才是此特性在.Net类库中的实际名称。

特性目标


特性目标是指应用特性的实体。 例如,特性可应用于类、特定方法或整个程序集。 默认情况下,特性应用于紧跟在它后面的元素。 不过,还可以进行显式标识。例如,可以标识为将特性应用于方法,还是应用于其参数或返回值。

若要显式标识特性目标,请使用以下语法:

[target : attribute-list]

下表列出了可能的 target 值。

目标值适用对象
assembly整个程序集
module当前程序集模块
field类或结构中的字段
event事件
method方法或 get 和 set 属性访问器
param方法参数或 set 属性访问器参数
propertyProperty
return方法、属性索引器或 get 属性访问器的返回值
type结构、类、接口、枚举或委托

示例:

// 将特性应用于程序集
[assembly: AssemblyTitleAttribute("Production assembly 4")]

// 将特性应用于模块
[module: CLSCompliant(true)]

// 将特性应用于方法1
[ValidatedContract]
int Method1() { return 0; }
// 将特性应用于方法2
[method: ValidatedContract]
int Method2() { return 0; }

// 将特性应用于方法参数
int Method3([ValidatedContract] string contract) { return 0; }

// 将特性应用于方法返回值
[return: ValidatedContract]
int Method4() { return 0; }

创建自定义特性


可通过定义特性类创建自己的自定义特性,特性类是直接或间接派生自 Attribute 的类,可快速轻松地识别元数据中的特性定义。 假设希望使用编写类型的程序员的姓名来标记该类型。 可能需要定义一个自定义 Author 特性类:

[System.AttributeUsage(System.AttributeTargets.Class |  
                       System.AttributeTargets.Struct,
                       AllowMultiple = true)  // multiuse attribute  
]  
public class AuthorAttribute : System.Attribute  
{  
    private string name;  
    public double version;  
  
    public AuthorAttribute(string name)  
    {  
        this.name = name;  
        version = 1.0;  
    }  
}

类名 AuthorAttribute 是该特性的名称,即 Author 加上 Attribute 后缀。 由于该类派生自 System.Attribute,因此它是一个自定义特性类。 构造函数的参数是自定义特性的位置参数。 在此示例中,name 是位置参数。 所有公共读写字段或属性都是命名参数。 在本例中,version 是唯一的命名参数。 请注意,使用 AttributeUsage 特性可使 Author 特性仅对 class 和 struct 声明有效。AllowMultiple 参数表示一次或多次使用此自定义特性。 

可按如下方式使用这一新特性:

[Author("P. Ackerman", version = 1.1)]  
class SampleClass  
{  
    // P. Ackerman's code goes here...  
}

在下面的代码示例中,某个类应用了同一类型的多个特性。

[Author("P. Ackerman", version = 1.1)]  
[Author("R. Koch", version = 1.2)]  
class SampleClass  
{  
    // P. Ackerman's code goes here...  
    // R. Koch's code goes here...  
}

使用反射访问特性


我们虽然可以通过自定义特性来将元数据或声明性信息与代码相关联,但是在没有检索该信息并对其进行操作的情况下将没有任何价值。 通过使用反射,可以检索通过自定义特性定义的信息。 主要方法是 GetCustomAttributes,它返回对象数组,这些对象在运行时等效于源代码特性。 此方法有多个重载版本。 有关详细信息,请参阅 Attribute

特性规范,例如:

[Author("P. Ackerman", version = 1.1)]  
class SampleClass

在概念上等效于此:

Author anonymousAuthorObject = new Author("P. Ackerman");  
anonymousAuthorObject.version = 1.1;

但是,在为特性查询 SampleClass 之前,代码将不会执行。 对 SampleClass 调用 GetCustomAttributes 会导致按上述方式构造并初始化一个 Author 对象。 如果该类具有其他特性,则将以类似方式构造其他特性对象。 然后 GetCustomAttributes 会以数组形式返回 Author 对象和任何其他特性对象。 之后你便可以循环访问此数组,根据每个数组元素的类型确定所应用的特性,并从特性对象中提取信息。

示例:

此处是一个完整的示例。 定义自定义特性、将其应用于多个实体,并通过反射对其进行检索。

// Multiuse attribute.  
[System.AttributeUsage(System.AttributeTargets.Class |  
                       System.AttributeTargets.Struct,  
                       AllowMultiple = true)  // Multiuse attribute.  
]  
public class Author : System.Attribute  
{  
    string name;  
    public double version;  
  
    public Author(string name)  
    {  
        this.name = name;  
  
        // Default value.  
        version = 1.0;  
    }  
  
    public string GetName()  
    {  
        return name;  
    }  
}  
  
// Class with the Author attribute.  
[Author("P. Ackerman")]  
public class FirstClass  
{  
    // ...  
}  
  
// Class without the Author attribute.  
public class SecondClass  
{  
    // ...  
}  
  
// Class with multiple Author attributes.  
[Author("P. Ackerman"), Author("R. Koch", version = 2.0)]  
public class ThirdClass  
{  
    // ...  
}  
  
class TestAuthorAttribute  
{  
    static void Test()  
    {  
        PrintAuthorInfo(typeof(FirstClass));  
        PrintAuthorInfo(typeof(SecondClass));  
        PrintAuthorInfo(typeof(ThirdClass));  
    }  
  
    private static void PrintAuthorInfo(System.Type t)  
    {  
        System.Console.WriteLine("Author information for {0}", t);  
  
        // Using reflection.  
        System.Attribute[] attrs = System.Attribute.GetCustomAttributes(t);  // Reflection.  
  
        // Displaying output.  
        foreach (System.Attribute attr in attrs)  
        {  
            if (attr is Author)  
            {  
                Author a = (Author)attr;  
                System.Console.WriteLine("   {0}, version {1:f}", a.name, a.version);  
            }  
        }  
    }  
}  
/* Output:  
    Author information for FirstClass  
       P. Ackerman, version 1.00  
    Author information for SecondClass  
    Author information for ThirdClass  
       R. Koch, version 2.00  
       P. Ackerman, version 1.00  
*/

使用特性创建 C/C++ 联合


通过使用特性,可自定义结构在内存中的布局方式。 例如,可使用 StructLayout(LayoutKind.Explicit) 和 FieldOffset 特性在 C/C++ 中创建所谓的联合。

示例:

在此代码段中,TestUnion 的所有字段均从内存中的同一位置开始。

// Add a using directive for System.Runtime.InteropServices.

[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]
struct TestUnion
{
    [System.Runtime.InteropServices.FieldOffset(0)]
    public int i;

    [System.Runtime.InteropServices.FieldOffset(0)]
    public double d;

    [System.Runtime.InteropServices.FieldOffset(0)]
    public char c;

    [System.Runtime.InteropServices.FieldOffset(0)]
    public byte b;
}

下面是另一个示例,其中的字段从不同的显式设置位置开始。

// Add a using directive for System.Runtime.InteropServices.

[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]
struct TestExplicit
{
    [System.Runtime.InteropServices.FieldOffset(0)]
    public long lg;

    [System.Runtime.InteropServices.FieldOffset(0)]
    public int i1;

    [System.Runtime.InteropServices.FieldOffset(0)]
    public int i2;

    [System.Runtime.InteropServices.FieldOffset(8)]
    public double d;

    [System.Runtime.InteropServices.FieldOffset(12)]
    public char c;

    [System.Runtime.InteropServices.FieldOffset(14)]
    public byte b;
}

两个整数字段 i1 和 i2 共享与 lg 相同的内存位置。 使用平台调用时,这种对结构布局的控制很有用。

参考:Microsofr C# 特性

内容概要:本文详细比较了GPU、TPU专用AI芯片在大模型推理优化方面的性能、成本及适用场景。GPU以其强大的并行计算能力和高带宽显存,适用于多种类型的神经网络模型和计算任务,尤其适合快速原型开发和边缘计算设备。TPU专为机器学习设计,擅长处理大规模矩阵运算密集型任务,如Transformer模型的推理,具有高吞吐量和低延迟特性,适用于自然语言处理和大规模数据中心的推理任务。专用AI芯片通过高度定制化架构,针对特定神经网络模型进行优化,如卷积神经网络(CNN),在处理特定任务时表现出色,同时具备低功耗和高能效比的优势,适用于边缘计算设备。文章还介绍了各自的优化工具和框架,如CUDA、TensorRT、TPU编译器等,并从硬件成本、运营成本和开发成本三个角度进行了成本对比。 适合人群:从事人工智能、深度学习领域的研究人员和技术人员,尤其是对大模型推理优化感兴趣的读者。 使用场景及目标:①帮助读者理解GPU、TPU和专用AI芯片在大模型推理中的优缺点;②为选择适合的硬件平台提供参考依据,以实现最优的推理性能和成本效益;③介绍各种优化工具和框架,帮助开发者高效部署和优化模型。 其他说明:本文不仅涵盖了硬件架构特性,还深入探讨了优化技术和应用场景,旨在为读者提供全面的技术参考。在选择硬件平台时,需综合考虑具体任务需求、预算限制及开发资源等因素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值