20250607-C#知识:特性

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、参考资料

  1. 《唐老狮C#》
  2. c# 调用c++ 动态链接库

本文结束,感谢您的阅读~

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值