【从零开始入门unity游戏开发之——C#篇40】C#特性(Attributes)和自定义特性

文章目录

  • 前言
  • 一、特性(`Attributes`)基本概念
  • 二、自定义特性
    • 1、自定义特性代码示例:
    • 2、应用自定义特性:
    • 3、解释
      • 3.1 **AttributeUsage 特性**
      • 3.2 特性的命名
      • 3.3 **构造函数**:
      • 3.4 **属性**:
    • 4、使用反射获取特性信息
    • 5、内置的系统特性
      • 5.1 **`[AttributeUsage]`**
      • 5.2 **`[Obsolete]`**
      • 5.3 **`[Serializable]`**
      • 5.4 **`[Conditional]`**
      • 5.5 **`[DllImport]`**
      • 5.6 **`[DebuggerDisplay]`**
      • 5.7`[Flags]`
    • 6、总结
  • 专栏推荐
  • 完结

前言

特性本身是通过类实现的,通常用于描述代码的某些方面(例如标记、验证、代码生成等),并可以在运行时通过反射获取。

一、特性(Attributes)基本概念

在 C# 中,特性 是一种附加信息或元数据,允许开发者向程序集、类、方法、属性、字段等添加自定义标记或信息。用于在代码中嵌入描述信息,通常用于描述代码结构(如类、方法、属性等)的附加信息。这些元数据在编译时嵌入程序集,并可以在运行时通过反射获取。

  • 本质:特性本质上是一个类,继承自 System.Attribute 类。
  • 用途:可以将特性应用于类、方法、属性、字段、参数等,作为额外的元数据或标记。
  • 反射:特性信息通常在运行时通过反射来访问。

二、自定义特性

为了定义一个特性,必须创建一个继承自 System.Attribute 的类。可以通过构造函数或者字段来传递数据。

1、自定义特性代码示例:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
public class MyCustomAttribute : Attribute
{public string Info { get; }// 构造函数public MyCustomAttribute(string info){Info = info;}// 可以在特性中定义方法public void DisplayInfo(){Console.WriteLine(Info);}
}

2、应用自定义特性:

[MyCustom("这是类的特性")]
public class MyClass
{[MyCustom("这是成员变量的特性")]public int Value;[MyCustom("这是方法的特性")]public void TestMethod(){Console.WriteLine("执行方法");}
}

3、解释

3.1 AttributeUsage 特性

  • AttributeTargets:这是一个必填参数,类型为 AttributeTargets 枚举的组合,指定了该特性可以应用于哪些程序元素。

    • All:所有可能的目标。
    • Assembly:程序集级别。
    • Class:类级别。
    • Constructor:构造函数级别。
    • Delegate:委托级别。
    • Enum:枚举级别。
    • Event:事件级别。
    • Field:字段级别。
    • Interface:接口级别。
    • Method:方法级别。
    • Module:模块级别(通常是指编译单元)。
    • Parameter:参数级别。
    • Property:属性级别。
    • ReturnValue:返回值级别(用于特性应用于方法返回值)。
    • Struct:结构体级别。

    你可以通过按位或运算符 (|) 来组合多个目标。例如:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class MyCustomAttribute : Attribute { }
    

    这表示 MyCustomAttribute 可以应用于方法

  • AllowMultiple:这是一个可选布尔参数,默认值为 false。如果设置为 true,则允许多个相同类型的特性应用于同一个程序元素。

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
    public class MultiUseAttribute : Attribute { }
    

    在这种情况下,你可以多次应用 MultiUseAttribute 到同一个类上

    [MultiUse("First use")]
    [MultiUse("Second use")]
    public class MyClass { }
    
  • Inherited:这也是一个可选布尔参数,默认值为 false。如果设置为 true,则子类会继承父类上的该特性;否则,子类不会继承这些特性。

    [AttributeUsage(AttributeTargets.Class, Inherited = true)]
    public class InheritableAttribute : Attribute { }
    

    在这种情况下,如果 DerivedClass 继承了 BaseClass,并且 BaseClass 上有 InheritableAttribute,那么 DerivedClass 也会被认为具有该特性:

    [Inheritable]
    public class BaseClass { }public class DerivedClass : BaseClass { }
    

3.2 特性的命名

在C#中,特性类名通常带有 Attribute 后缀,但在应用时可以省略。例如:

public class MyCustomAttribute : Attribute { }

然而,在实际应用这个特性时,你可以选择省略 Attribute 后缀。编译器会自动识别并匹配到相应的特性类。因此,以下两种写法是等价的:

[MyCustom("这是一个测试")]
public class MyClass { }[MyCustomAttribute("这是一个测试")]
public class MyClass { }

这种灵活性使得特性名称更加简洁,尤其是在频繁使用的情况下。不过,为了保持代码的一致性和可读性,建议在定义特性时始终使用完整的 Attribute 后缀,而在应用时可以选择省略

3.3 构造函数

构造函数用于初始化特性实例,并接受参数以传递给特性。通过构造函数,你可以在应用特性时提供必要的信息。构造函数可以包含任意数量的参数,但通常应该尽量保持简单,以便易于使用。

示例:带参数的构造函数

public class MyCustomAttribute : Attribute
{public string Description { get; }// 构造函数接受一个字符串参数,并将其赋值给Description属性public MyCustomAttribute(string description){Description = description;}
}

在这个例子中,当你应用 MyCustom 特性时,必须提供一个字符串参数:

[MyCustom("这是一个测试类")]
public class MyClass { }

如果你需要传递多个参数,可以在构造函数中添加更多的参数:

public class MyCustomAttribute : Attribute
{public string Description { get; }public int Priority { get; }public MyCustomAttribute(string description, int priority){Description = description;Priority = priority;}
}// 应用特性时传递两个参数
[MyCustom("这是一个测试类", priority: 1)]
public class MyClass { }

3.4 属性

属性用于存储特性接收到的数据,并可以在运行时通过反射访问这些数据。你可以定义任意数量的公共属性来保存不同类型的配置信息。

示例:定义和使用属性

public class MyCustomAttribute : Attribute
{public string Description { get; }public int Priority { get; }// 构造函数初始化属性public MyCustomAttribute(string description, int priority){Description = description;Priority = priority;}// 可选:定义只读或读写属性public bool IsEnabled { get; set; } = true;
}// 应用特性时设置属性
[MyCustom("这是一个测试类", priority: 1, IsEnabled = false)]
public class MyClass { }

注意,对于非构造函数参数的属性(如 IsEnabled),你需要在应用特性时使用命名参数的形式来设置它们。

4、使用反射获取特性信息

你可以通过反射来访问类型或成员上应用的特性。在运行时,可以使用 Attribute.GetCustomAttributesMemberInfo.GetCustomAttributes 方法来检索已应用的特性。

获取特性信息代码示例:

using System;
using System.Reflection;[AttributeUsage(AttributeTargets.Method)]
public class CustomMethodAttribute : Attribute
{public string Info { get; }public CustomMethodAttribute(string info){Info = info;}
}public class MyClass
{[CustomMethodAttribute("This is a custom method")]public void MyMethod(){Console.WriteLine("MyMethod executed.");}
}class Program
{static void Main(){// 获取 MyClass 类型上的所有方法MethodInfo methodInfo = typeof(MyClass).GetMethod("MyMethod");// 获取 MyMethod 上的 CustomMethodAttribute 特性CustomMethodAttribute attribute = (CustomMethodAttribute)Attribute.GetCustomAttribute(methodInfo, typeof(CustomMethodAttribute));if (attribute != null){Console.WriteLine(attribute.Info);  // 输出: This is a custom method}}
}

5、内置的系统特性

C# 提供了许多内置的系统特性(Attributes),这些特性可以帮助开发者更高效地编写代码,控制编译器行为,以及与各种框架和工具进行交互。以下是C#中一些常用的系统内置特性及其应用场景。

5.1 [AttributeUsage]

前介绍的AttributeUsage其实就是一个内置的系统特性,用于定义自定义特性的应用范围和其他行为。前面已经详细介绍过这个特性。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MyCustomAttribute : Attribute { }

5.2 [Obsolete]

用于标记过时的方法、类或其他成员,告知其他开发者不要使用它们,并可以在必要时引发编译警告或错误。

//第一个参数是提示信息
//第2个参数可选,默认false,表示警告, true 表示这会导致编译错误
[Obsolete("请使用新方法代替", true)] 
public void OldMethod() { }

效果
在这里插入图片描述

5.3 [Serializable]

指示类可以序列化,意味着该类的实例可以转换为字节流以便存储或传输。

[Serializable]
public class Person
{public string Name { get; set; }public int Age { get; set; }
}

解释:如果你希望把一个类的对象保存到文件或者通过网络发送出去,就需要用到这个特性。它告诉程序:“我可以被转换成数据流”。

5.4 [Conditional]

指定条件编译符号,只有当符号定义时才会编译相应的方法调用。常用于调试信息记录。

[Conditional("DEBUG")]
public void DebugLog(string message)
{Console.WriteLine(message);
}

解释:当你只在某些条件下才想执行某个方法时(比如调试模式下记录日志),可以用这个特性。它告诉编译器只有当条件满足时才包含这段代码。

[Conditional] 特性和前面我们介绍得预编译指令 #if, #endif实现效果类似,翻译成预编译指令如下:

#if DEBUG
public void DebugLog(string message)
{Console.WriteLine(message);
}
#endif

当你在方法上应用 [Conditional] 特性时,编译器会根据你提供的条件符号来决定是否包含对该方法的调用。如果定义了该符号(这里是DEBUG符合),则保留调用;否则,编译器会忽略这些调用,就好像它们不存在一样。

5.5 [DllImport]

主要用于调用外部库。

using System.Runtime.InteropServices;[DllImport("user32.dll", SetLastError = true)]
static extern bool SetCursorPos(int X, int Y);

解释:有时候你需要调用一些不是用C#写的函数(比如Windows系统的功能),这时就可以用这个特性。它告诉程序去哪里找这些函数。

之前开发桌面宠物时有时使用过很多,感兴趣可以看看:
【制作100个unity游戏之32】unity开发属于自己的一个2d/3d桌面宠物,可以实时计算已经获取的工资

5.6 [DebuggerDisplay]

用于自定义调试器显示的信息,使得在调试时更容易查看对象的状态。

[DebuggerDisplay("Name = {Name}, Age = {Age}")]
public class Person
{public string Name { get; set; }public int Age { get; set; }
}

比如,实际其实没啥用
在这里插入图片描述

5.7[Flags]

当你在枚举上应用 [Flags] 特性时,它告诉编译器和开发者这个枚举的值可以按位组合(即通过按位或运算 | 组合多个值)。这样,你可以用一个单一的变量来表示多个选择,而不是为每个选择创建单独的变量。

[Flags]
public enum FileAccess
{Read = 1,Write = 2,ReadWrite = Read | Write
}

在这个例子中,FileAccess 枚举的值被设计成可以组合使用。例如,ReadWrite 是由 ReadWrite 组合而成。

6、总结

学习 C# 自定义特性的意义在于它能够极大地增强代码的灵活性、可扩展性以及可读性。通过自定义特性(Attributes),开发者可以在代码中添加元数据,并通过反射等机制动态地处理这些元数据,进而实现一些通用的功能和行为。这种技术在大型系统、框架和库中尤为重要。

可能在初期看不出太多具体的应用场景,但随着后面对 Unity 引擎的深入了解,你会发现自定义特性在项目中的强大作用。在 Unity 中,也自定义了很多默认的特性,很大程度上增强编辑器和游戏逻辑的灵活性和可扩展性,这个知识就留到【unity篇】再讲解了。


专栏推荐

地址
【从零开始入门unity游戏开发之——C#篇】
【从零开始入门unity游戏开发之——unity篇】
【制作100个Unity游戏】
【推荐100个unity插件】
【实现100个unity特效】
【unity框架开发】

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/891711.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python学习(六)——While、for、循环控制、指数爆炸】

Python学习(六)——While、for、循环控制、指数爆炸 本文介绍了While、for、循环控制、指数爆炸,仅作为本人学习时记录,感兴趣的初学者可以一起看看,欢迎评论区讨论,一起加油鸭~~~ 心中默念:Py…

基于PyQt5的UI界面开发——图像与视频的加载与显示

介绍 这里我们的主要目标是实现一个基于PyQt5和OpenCV的图像浏览和视频播放应用。用户可以选择本地的图像或视频文件夹,进行图像自动播放和图像切换以及视频播放和调用摄像头等操作,并且支持图像保存功能。项目的核心设计包括文件路径选择、图像或视频的…

云手机+Facebook:让科技与娱乐完美结合

移动互联网时代,Facebook作为全球最大的社交媒体平台之一,早已成为企业、品牌和组织竞相角逐的营销阵地。而云手机的出现,则为Facebook营销注入了新的活力,其独特的优势让营销活动更加高效、精准且灵活。本文将深入探讨云手机在Fa…

全新免押租赁系统打造便捷安全的租赁体验

内容概要 全新免押租赁系统的推出,标志着租赁行业的一次重大变革。这个系统的最大特点就是“免押金”,大大减轻了用户在租赁过程中的经济负担。从此,不再需要为一部手机或其他商品支付高昂的押金,用户只需通过简单的信用评估&…

postman在软件测试中的应用

postman工具概述 Postman 是一款功能强大的 API 开发和测试工具,在软件开发和测试领域应用广泛。开发阶段,可以通过工具进行mock数据测试,方便开发,联调;测试阶段,可以通过不同环境,不同数据进…

电子电气架构 --- 安全相关内容汇总

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所谓鸡汤,要么蛊惑你认命,要么怂恿你拼命,但都是回避问题的根源,以现象替代逻辑,以情绪代替思考,把消极接受现实的懦弱,伪装成乐观面对不幸的…

探索Wiki:开源知识管理平台及其私有化部署

在如今的信息时代,企业和团队的知识管理变得愈发重要。如何有效地存储、整理、共享和协作,是提高团队效率和创新能力的关键因素之一。今天,我要为大家介绍一款非常有用的github上开源知识管理工具——Wiki,并分享它的私有化部署方…

一份完整的软件测试报告如何编写?

在软件开发的过程中,测试是必不可少的环节。然而,测试报告往往是最被忽视的部分。你是否也曾在忙碌的测试工作后,面对一份模糊不清的测试报告感到头疼?一份清晰、完整且结构合理的测试报告,能够帮助团队快速了解软件的…

OpenEuler22.03 LTS SP3 系统优化

OpenEuler22.03 LTS SP3 系统优化 1、关闭selinux setenforce 0 sed -i "s#SELINUXenforcing#SELINUXdisabled#g" /etc/selinux/config 2、禁用swap swapoff -a sed -ri s/.*swap.*/#&/ /etc/fstab 3、公有云机器,必须安全加固 (1&…

logback之自定义过滤器

logback有两种过滤器,一种是context中的过滤器叫TurboFilter,是一个全局的过滤器,会影响所有的日志记录。另一种是Appender中的过滤器,只对所在的append有效。两者大同小异,这里我们以Appender的过滤器为例。 &#x…

HackMyVM-Airbind靶机的测试报告

目录 一、测试环境 1、系统环境 2、使用工具/软件 二、测试目的 三、操作过程 1、信息搜集 2、Getshell 3、提权 使用ipv6绕过iptables 四、结论 一、测试环境 1、系统环境 渗透机:kali2021.1(192.168.101.127) 靶 机:debian(192.168.101.11…

前端,npm install安装依赖卡在sill idealTree buildDeps(设置淘宝依赖)

输入npm i后,一直卡在sill idealTree buildDeps,一动不动 cnpm可以安装成功,但使用cnpm不会生成package-lock.json文件 设置淘宝依赖,依然卡住,挂梯子也不行 解决方法: // 取消ssl验证 set strict-ssl …

FPGA、STM32、ESP32、RP2040等5大板卡,结合AI,更突出模拟+数字+控制+算法

板卡选择困难症了?如果你也想玩FPGA、STM32、ESP32、RP2040相关的板卡,不如看看以下几款板卡,如果正巧碰上能实现你想要做的项目呢~ 01 小脚丫FPGA STEP BaseBoard V4.0套件 STEP BaseBoard V4.0是第4代小脚丫FPGA扩展底板(点击了…

go项目zero框架中表字段日期设置的几种格式对比与实践

在 GoZero 框架中,日期字段的格式通常取决于你的应用场景以及所使用的数据库类型。在 GoZero 中,日期字段的设置方式一般有两种:通过 time.Time 类型和通过字符串(例如 string 或 int64)。每种方式有其适用的场景和优缺…

UE5动画蓝图

动画蓝图,混合空间,状态机,瞄准偏移,动画蒙太奇,动画混合,骨骼绑定,动画重定向,动画通知,Control Rig…… 虚幻动画模块是一个庞大的系统,大模块里又包含很多…

XIAO Esp32S3制作网络摄像头——1音频获取

1、功能介绍 本文主要是基于XIAO Esp32S3(Sense)做的一款网络摄像头,主要包含以下功能 1 音频获取/保存 2 视频获取/视频保存 3 行人检测/火焰检测/行人追踪(告警) 4 指定区域 5 摄像头旋转 。。。 本文主要实现第一步,音频获取,后续会陆续实现后面的功能,敬请期…

Windows安装了pnpm后无法在Vscode中使用

Windows安装了pnpm后无法在Vscode中使用 解决方法: 以管理员身份打开 PowerShell 并执行以下命令后输入Y回车即可。 Set-ExecutionPolicy RemoteSigned -Scope CurrentUser之后就可以正常使用了

Leffa 虚拟试衣论文笔记

Leffa: Learning Flow Fields in Attention for Controllable Person Image Generation https://github.com/xuanandsix/awesome-virtual-try-on-note/tree/main/Leffa 打开链接查看详情,更多虚拟试穿论文持续更新。

qt5.15.2+visual studio2022 免安装版环境配置

1.环境准备 visual studio2022qt5.15.2(免安装版本) 2.环境配置 2.1 打开首选项 2.2 添加Qt版本 2.3 构建套件手动添加Qt 5.15.2(msvc2019_64)并配置如下 3.新建项目 问题1:qt creator 没有欢迎界面 解决办法&#…

手写顺序流程图组件

效果图 完整代码 <template><div><div class"container" :style"{ width: ${spacingX * (colNum - 1) itemWidth * colNum}px }"><divv-for"(item, i) in recordList":key"i"class"list-box":style&…