C#特性
一个小例程:
using System;
using System.Linq;
using System.Reflection;
/*
* 1. 自定义特性定义
* - AttributeUsage: 定义特性的应用范围和规则
* - 继承自System.Attribute: 所有自定义特性的基类
*/
[AttributeUsage(
AttributeTargets.Class | // 可应用于类
AttributeTargets.Constructor | // 可应用于构造方法
AttributeTargets.Field | // 可应用于字段
AttributeTargets.Method | // 可应用于方法
AttributeTargets.Property, // 可应用于属性
AllowMultiple = true // 允许在同一目标上多次应用
)]
public class DeBugInfo : System.Attribute // 必须继承自Attribute类
{
// 2. 私有字段 - 存储核心数据
private int bugNo; // Bug编号
private string developer; // 开发者名称
private string lastReview; // 最后审查日期
private string message; // 额外消息(修复递归问题后)
/*
* 3. 构造函数
* - 定位参数:必须通过构造函数传递的参数
* - 这些参数在应用特性时必须按顺序提供
*/
public DeBugInfo(int bg, string dev, string d)
{
bugNo = bg; // 设置Bug编号
developer = dev; // 设置开发者
lastReview = d; // 设置审查日期
}
// 4. 只读属性 - 提供私有字段的安全访问
public int BugNo => bugNo; // 只读访问Bug编号
public string Developer => developer; // 只读访问开发者
public string LastReview => lastReview;// 只读访问审查日期
/*
* 5. 读写属性 - 解决递归问题
* 原始版本错误:使用属性自身导致无限递归
* 修复方案:使用私有字段作为存储
*/
public string Message
{
get => message; // 获取消息内容
set => message = value; // 设置消息内容
}
}
/*
* 6. 应用特性的目标类
* - 类上应用多个DebugInfo特性(AllowMultiple=true允许)
* - 混合使用定位参数和命名参数
*/
[DeBugInfo(101,"张三", "2023-10-01", Message = "初始化逻辑")]
[DeBugInfo(202, "李四", "2023-11-15", Message = "性能优化")]
public class Calculator
{
// 7. 方法上应用特性(只使用定位参数)
[DeBugInfo(305, "王五", "2023-12-20")]
public int Add(int a, int b) => a + b;
// 8. 方法上应用特性(混合定位和命名参数)
[DeBugInfo(404, "赵六", "2024-01-10", Message = "边界处理")]
public int Multiply(int a, int b) => a * b;
}
class Program
{
static void Main()
{
// 9. 反射获取类型信息
Type calcType = typeof(Calculator);
// 10. 读取类上的所有DebugInfo特性
Console.WriteLine("【类特性】");
var classAttrs = calcType.GetCustomAttributes<DeBugInfo>();
foreach (var attr in classAttrs)
{
Console.WriteLine($"Bug#{attr.BugNo} | 开发者:{attr.Developer} " +
$"| 审查:{attr.LastReview} | 信息:{attr.Message ?? "(无)"}");
}
// 11. 读取所有方法上的特性
Console.WriteLine("\n【方法特性】");
foreach (var method in calcType.GetMethods(BindingFlags.Public | BindingFlags.Instance))
{
// 12. 获取方法上的DebugInfo特性
var methodAttrs = method.GetCustomAttributes<DeBugInfo>();
if (!methodAttrs.Any()) continue; // 跳过无特性的方法
Console.WriteLine($"方法: {method.Name}");
foreach (var attr in methodAttrs)
{
// 13. 显示特性信息(包含空消息处理)
Console.WriteLine($" Bug#{attr.BugNo} | 开发者:{attr.Developer} " +
$"| 审查:{attr.LastReview} | 信息:{attr.Message ?? "(无)"}");
}
}
}
}
输出结果:
【类特性】
Bug#101 | 开发者:张三 | 审查:2023-10-01 | 信息:初始化逻辑
Bug#202 | 开发者:李四 | 审查:2023-11-15 | 信息:性能优化【方法特性】
方法: Add
Bug#305 | 开发者:王五 | 审查:2023-12-20 | 信息:(无)
方法: Multiply
Bug#404 | 开发者:赵六 | 审查:2024-01-10 | 信息:边界处理
C#代码主要内容解析
提供的C#代码主要演示了自定义特性的定义、应用和反射读取,包含以下核心内容:
1. 自定义特性定义(DeBugInfo)
-
继承与元数据:
DeBugInfo继承自System.Attribute,使用[AttributeUsage]指定可应用于类、构造器、字段、方法和属性(AllowMultiple=true允许多次应用)。 -
数据结构:
-
私有字段:
bugNo(Bug编号)、developer(开发者)、lastReview(审查日期)、message(消息)。 -
构造函数:接受定位参数(
int bg, string dev, string d)。 -
属性:
BugNo、Developer、LastReview为只读;Message为读写(修复了递归问题)。
-
2. 特性应用示例(Calculator 类)
-
类级别应用:
应用两个DeBugInfo特性C#
[DeBugInfo(101,"张三","2023-10-01", Message="初始化逻辑")] [DeBugInfo(202,"李四","2023-11-15", Message="性能优化")] -
方法级别应用:
-
Add方法:使用定位参数[DeBugInfo(305,"王五","2023-12-20")]。 -
Multiply方法:混合定位和命名参数[DeBugInfo(404,"赵六","2024-01-10", Message="边界处理")]。
-
3. 反射读取特性(Program.Main 方法)
-
类特性读取:
通过typeof(Calculator).GetCustomAttributes<DeBugInfo>()获取所有类特性,遍历输出信息(如Bug编号、开发者)。 -
方法特性读取:
使用GetMethods()获取公共实例方法,结合GetCustomAttributes<DeBugInfo>()和Any()跳过无特性方法: -
C#
var methodAttrs = method.GetCustomAttributes<DeBugInfo>(); if (!methodAttrs.Any()) continue; // 关键过滤逻辑 -
输出处理:
对每个特性打印格式化信息(含空消息处理attr.Message ?? "(无)")。
using System;
using System.Reflection;
namespace c.biancheng.net
{
//要分配给类及其成员的自定义属性错误修正程序
[AttributeUsage(
AttributeTargets.Class|
AttributeTargets.Constructor|
AttributeTargets.Field|
AttributeTargets.Method|
AttributeTargets.Property,
AllowMultiple =true)]
public class DeBugInfo : System.Attribute
{
private int bugNo;
private string developer;
private string lastReview;
public string message;
public DeBugInfo(int bg,string dev,string d) {
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}
public int BugNo
{
get
{
return bugNo;
}
}
public string Developer
{
get
{
return developer;
}
}
public string LastReview
{
get
{
return lastReview;
}
}
public string Message
{
get
{
return message;
}
set
{
message = value;
}
}
}
[DeBugInfo(45,"Zara Ali","12/8/2012",Message ="返回类型不匹配")]
[DeBugInfo(49,"JUha Ali","10/10/2021",Message ="变量未使用")]
class Rectangle
{
//成员变量
protected double length;
protected double width;
public Rectangle(double length, double width)
{
this.length = length;
this.width = width;
}
[DeBugInfo(55,"Zara Ali","19/10/2012",Message ="返回值类型不匹配")]
public double GetArea()
{
return length*width;
}
[DeBugInfo(56,"Zara Ali","19/10/2012")]
public void Display()
{
Console.WriteLine("长: {0}", length);
Console.WriteLine("宽: {0}", width);
Console.WriteLine("面积: {0}", GetArea());
}
}
class Demo
{
static void Main(string[] args)
{
Rectangle r = new Rectangle(4.5, 7.5);
r.Display();
Type type = typeof(Rectangle);
//遍历Rectangle
foreach (Object attributes in type.GetCustomAttributes(false))
{
DeBugInfo dbi = (DeBugInfo)attributes;
if (null != dbi)
{
Console.WriteLine("Bug 编号: {0}", dbi.BugNo);
Console.WriteLine("开发者: {0}", dbi.Developer);
Console.WriteLine("上次审核时间: {0}", dbi.LastReview);
Console.WriteLine("评论: {0}", dbi.Message);
}
}
// 遍历函数属性
foreach (MethodInfo m in type.GetMethods())
{
foreach (Attribute a in m.GetCustomAttributes(true))
{
DeBugInfo dbi = (DeBugInfo)a;
if (null != dbi)
{
Console.WriteLine("Bug 编号: {0}, 函数名: {1}", dbi.BugNo, m.Name);
Console.WriteLine("开发者: {0}", dbi.Developer);
Console.WriteLine("上次审核时间: {0}", dbi.LastReview);
Console.WriteLine("评论: {0}", dbi.Message);
}
}
}
Console.ReadLine();
}
}
}
运行结果:
长: 4.5
宽: 7.5
面积: 33.75
Bug 编号: 45
开发者: Zara Ali
上次审核时间: 12/8/2012
评论: 返回类型不匹配
Bug 编号: 49
开发者: JUha Ali
上次审核时间: 10/10/2021
评论: 变量未使用
Bug 编号: 55, 函数名: GetArea
开发者: Zara Ali
上次审核时间: 19/10/2012
评论: 返回值类型不匹配
Bug 编号: 56, 函数名: Display
开发者: Zara Ali
上次审核时间: 19/10/2012
评论:
Unhandled exception. System.InvalidCastException: Unable to cast object of type 'System.Runtime.CompilerServices.NullableContextAttribute' to type 'c.biancheng.net.DeBugInfo'.
at c.biancheng.net.Demo.Main(String[] args) in D:\Project_Doc\ConsoleApp1\ConsoleApp1\Program.cs:line 133
代码解释:该代码通过反射遍历类型(Rectangle类)及其方法的自定义特性(DeBugInfo)。
代码分为两部分:遍历类上的特性、遍历每个方法上的特性。
第一部分:遍历类特性
-
使用
type.GetCustomAttributes(false)获取直接应用于类的特性(不搜索继承链)。 -
将每个特性强制转换为
DeBugInfo类型,并输出相关信息。
第二部分:遍历方法特性
-
遍历类型的所有方法(
type.GetMethods()默认返回公共实例方法)。 -
对每个方法,使用
m.GetCustomAttributes(true)获取所有特性(包括继承的特性)。 -
同样将特性转换为
DeBugInfo并输出,同时输出方法名。
注意:代码中使用了两个循环嵌套来遍历方法和其特性。
typeof 在编译时确定类型(不需要实例) Type t1 = typeof(Rectangle);
GetType() 在运行时确定实际类型(需要实例)
// typeof 在编译时确定类型(不需要实例) Type t1 = typeof(Rectangle); // GetType() 在运行时确定实际类型(需要实例) Rectangle rect = new Rectangle(); Type t2 = rect.GetType();
索引器的重载
索引器可以被重载,而且在声明索引器时也可以带有多个参数,每个参数可以是不同的类型。另外,索引器中的索引不必是整数,也可以是其他类型,例如字符串类型。
using System;
// 创建一个类来存储字符串键值对
public class StringIndexerExample
{
private string[,] data = new string[3, 2]; // 存储数据的二维数组
public StringIndexerExample()
{
// 初始化一些数据
data[0, 0] = "Apple";
data[0, 1] = "Red";
data[1, 0] = "Banana";
data[1, 1] = "Yellow";
data[2, 0] = "Grape";
data[2, 1] = "Purple";
}
// 索引器重载1:使用整数作为索引
public string this[int index]
{
get
{
if (index >= 0 && index < data.GetLength(0))
{
return data[index, 0]; // 返回水果名称
}
else
{
throw new IndexOutOfRangeException("索引超出范围");
}
}
}
// 索引器重载2:使用字符串作为索引
public string this[string key]
{
get
{
for (int i = 0; i < data.GetLength(0); i++)
{
if (data[i, 0].Equals(key, StringComparison.OrdinalIgnoreCase))
{
return data[i, 1]; // 返回颜色
}
}
return null; // 如果没有找到,则返回null
}
}
// 索引器重载3:使用两个整数作为索引
public string this[int row, int column]
{
get
{
if (row >= 0 && row < data.GetLength(0) && column >= 0 && column < data.GetLength(1))
{
return data[row, column];
}
else
{
throw new IndexOutOfRangeException("索引超出范围");
}
}
}
}
class Program
{
static void Main(string[] args)
{
StringIndexerExample example = new StringIndexerExample();
// 使用整数索引访问
Console.WriteLine("第一个水果: " + example[0]);
// 使用字符串索引访问
Console.WriteLine("Banana的颜色: " + example["Banana"]);
// 使用两个整数索引访问
Console.WriteLine("第二个元素的第二列值: " + example[1, 1]);
}
}
更多推荐



所有评论(0)