C#知识:特性
1、特性简介
- 特效本质上是个类
- 可以利用特性给类、成员变量、方法、方法参数等元数据添加额外自定义信息
- 运行时可以通过反射获取特性
- 有点类似Java的注解
2、自定义特性
- 自定义一个类,继承Attribute类
- 特性名要以Attribute结尾
class MyAttribute : Attribute
{
public string info;
public MyAttribute(string info) => this.info = info;
public void PrintAttributeInfo() => Console.WriteLine(info);
}
3、使用特性
- 使用特性本质上就是调用特性的构造函数
- 基本语法:[特性名(参数列表)]
- 可以写在类、函数、成员变量、函数参数上
[MyAttribute("自定义类")]
class Employee:Person
{
[MyAttribute("自定义成员变量")]
public string name;
public int id;
public Employee(string name, int id)
{
this.name = name;
this.id = id;
}
[MyAttribute("自定义成员方法")]
public void PrintInfo([MyAttribute("自定义方法参数")] string word)
{
Console.WriteLine(name);
Console.WriteLine(id);
Console.WriteLine(word);
}
}
4、利用反射获取特性信息
4.1 判断特性是否被使用
IsDefined方法
- 参数1:特性类型
- 参数2:是否搜索继承链(属性和事件忽略此参数),向上搜索(包含基类和接口的特性)
Employee employee = new Employee("张飞", 3);
Type type = employee.GetType();
Console.WriteLine(type.IsDefined(typeof(MyAttribute), false));
//Person没有父类,Person本身没有添加特性,所以无论是否搜索继承链都返回false
Person person = new Person();
Type type2 = person.GetType();
Console.WriteLine(type2.IsDefined(typeof(MyAttribute), false));
Console.WriteLine(type2.IsDefined(typeof(MyAttribute), true));
//GoodEmployee类本身没有添加特性,但是它的父类添加了,所以当搜索继承链时会返回true(要求子类能够自动继承父类特性),否则返回false
GoodEmployee goodEmployee = new GoodEmployee("关羽", 2);
Type type3 = goodEmployee.GetType();
Console.WriteLine(type3.IsDefined(typeof(MyAttribute), false));
Console.WriteLine(type3.IsDefined(typeof(MyAttribute), true));
class GoodEmployee : Employee
{
public GoodEmployee(string name, int id) : base(name, id)
{
}
}
class Person
{
}
输出

4.2 获取元数据中的所有特性
object[] attributes = type.GetCustomAttributes(true);
for(int i = 0; i < attributes.Length; i++)
{
if (attributes[i] is MyAttribute)
(attributes[i] as MyAttribute).PrintAttributeInfo();
else
Console.WriteLine(attributes[i]);
}
输出

5、给自定义特性添加限制条件
未显式指定 AttributeUsage 时,等价于:
[AttributeUsage(
AttributeTargets.All,
AllowMultiple = false,
Inherited = true
)]
class MyAttribute : Attribute
{
public string info;
public MyAttribute(string info) => this.info = info;
public void PrintAttributeInfo() => Console.WriteLine(info);
}
显示指定限制条件:
- 第一个参数:特性可以加到哪些地方
- 第二个参数:是否允许在一个地方重复添加相同特性
- 第三个参数:是否允许特性被子类自动继承
[AttributeUsage(
AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Method,
AllowMultiple = false,
Inherited = true
)]
class MyAttribute : Attribute
{
public string info;
public MyAttribute(string info) => this.info = info;
public void PrintAttributeInfo() => Console.WriteLine(info);
}
6、系统提供的特性
6.1 过时特性Obsolete
- 参数1:调用过时方法时的提示信息
- 参数2:true表示使用方法会报错,false表示使用方法时会警告
[Obsolete("该方法已过时,请换一个Please",false)]
public void OldMethod()
{
}
employee.OldMethod();

6.2 调用者信息特性
- 修饰的参数要使用默认值
- CallerFilePath:调用文件路径
- CallerLineNumber:调用行数
- CallerMemberName:调用函数
public void PrintCaller([CallerFilePath]string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0, [CallerMemberName] string callerMemberName = "")
{
Console.WriteLine($"调用文件路径:{callerFilePath}");
Console.WriteLine($"调用行数:{callerLineNumber}");
Console.WriteLine($"调用函数:{callerMemberName}");
}
employee.PrintCaller();
运行结果

6.3 条件编译特性
- 与#define配合使用
- 如果不满足条件,则代码不会执行
在方法上添加该特性
[Conditional("DD")]
static void ConditionalFun() => Console.WriteLine("条件方法执行!");
如果有以下预处理,则方法运行,否则不运行
#define DD
ConditionalFun();
添加了#define DD代码后的运行结果:

6.4 外部DLL包特性
- 用来标记非C#方法,表明该方法是在外部的DLL中定义
- 一般用来调用C/C++写好的代码
先创建一个C++编写的动态链接库.dll文件

编写头文件pch.h

编写源文件pch.cpp

点击生成.dll文件:TestDLL.dll


把刚刚生成的TestDLL.dll移动到指定目录C#项目的指定目录中

在C#项目中编写代码来调用.dll文件中的方法
[DllImport("TestDLL.dll")]
static extern int Mul(int a, int b);
Console.WriteLine(Mul(2, 5));
运行结果

7、完整代码示例
C#源文件
#define DD
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LearnAttribute
{
// 未显式指定 AttributeUsage 时,等价于:
//[AttributeUsage(
// AttributeTargets.All,
// AllowMultiple = false,
// Inherited = true
//)]
[AttributeUsage(
AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Method,
AllowMultiple = false,
Inherited = true
)]
//第一个参数:特性可以加到哪些地方
//第二个参数:是否允许在一个地方重复添加相同特性
//第三个参数:是否允许特性被子类自动继承
class MyAttribute : Attribute
{
public string info;
public MyAttribute(string info) => this.info = info;
public void PrintAttributeInfo() => Console.WriteLine(info);
}
//[MyAttribute("自定义类")]
[MyAttribute("自定义类")]
class Employee:Person
{
[MyAttribute("自定义成员变量")]
public string name;
public int id;
public Employee(string name, int id)
{
this.name = name;
this.id = id;
}
[MyAttribute("自定义成员方法")]
public void PrintInfo([MyAttribute("自定义方法参数")] string word)
{
Console.WriteLine(name);
Console.WriteLine(id);
Console.WriteLine(word);
}
[Obsolete("该方法已过时,请换一个Please",false)]
public void OldMethod()
{
}
//CallerFilePath:调用文件路径
//CallerLineNumber:调用行数
//CallerMemberName:调用函数
public void PrintCaller([CallerFilePath]string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0, [CallerMemberName] string callerMemberName = "")
{
Console.WriteLine($"调用文件路径:{callerFilePath}");
Console.WriteLine($"调用行数:{callerLineNumber}");
Console.WriteLine($"调用函数:{callerMemberName}");
}
}
class GoodEmployee : Employee
{
public GoodEmployee(string name, int id) : base(name, id)
{
}
}
class Person
{
}
internal class Program
{
[Conditional("DD")]
static void ConditionalFun() => Console.WriteLine("条件方法执行!");
[DllImport("TestDLL.dll")]
static extern int Mul(int a, int b);
static void Main(string[] args)
{
Employee employee = new Employee("张飞", 3);
Type type = employee.GetType();
//判断是否使用了指定特性
//参数1:特性类型
//参数2:是否搜索继承链(属性和事件忽略此参数),向上搜索(包含基类和接口的特性)
Console.WriteLine(type.IsDefined(typeof(MyAttribute), false));
//Person没有父类,Person本身没有添加特性,所以无论是否搜索继承链都返回false
Person person = new Person();
Type type2 = person.GetType();
Console.WriteLine(type2.IsDefined(typeof(MyAttribute), false));
Console.WriteLine(type2.IsDefined(typeof(MyAttribute), true));
//GoodEmployee类本身没有添加特性,但是它的父类添加了,所以当搜索继承链时会返回true(要求子类能够自动继承父类特性),否则返回false
GoodEmployee goodEmployee = new GoodEmployee("关羽", 2);
Type type3 = goodEmployee.GetType();
Console.WriteLine(type3.IsDefined(typeof(MyAttribute), false));
Console.WriteLine(type3.IsDefined(typeof(MyAttribute), true));
//获取元数据中的所有特性
object[] attributes = type.GetCustomAttributes(true);
for(int i = 0; i < attributes.Length; i++)
{
if (attributes[i] is MyAttribute)
(attributes[i] as MyAttribute).PrintAttributeInfo();
else
Console.WriteLine(attributes[i]);
}
//系统特性
//过时特性Obsolete
employee.OldMethod();
//调用者信息特性
employee.PrintCaller();
//条件编译特性
ConditionalFun();
//外部DLL包特性
Console.WriteLine(Mul(2, 5));
}
}
}
C++源文件和头文件
// pch.h
#ifndef PCH_H
#define PCH_H
#include "framework.h"
extern "C" _declspec(dllexport) int Mul(int, int);
#endif //PCH_H
// pch.cpp
#include "pch.h"
int Mul(int a, int b)
{
return a * b;
}
8、参考资料
- 《唐老狮C#》
- c# 调用c++ 动态链接库
本文结束,感谢您的阅读~


被折叠的 条评论
为什么被折叠?



