当前位置: 首页 > news >正文

03-C#.Net-特性-面试题

Q1:特性和注释有什么区别?特性的本质是什么?

出题意图: 考察对特性基础概念的理解,区分"会用"和"真懂"。

答:

注释在编译后不存在,只是给人看的文字说明。特性编译后以元数据形式存储在程序集中,运行时可以通过反射读取,能携带数据和逻辑,真正影响程序行为。

特性的本质是一个类,直接或间接继承自 System.Attribute。用 [] 标记在代码元素上,本质上是在调用这个类的构造函数。

// 这两行等价
[Custom(123)]
public class Student { }// 编译器实际做的事:在元数据中记录 new CustomAttribute(123)

解答思路: 先说核心区别(编译后是否存在),再说本质(是一个类),最后点出标记语法的本质是调用构造函数。


Q2:AttributeUsage 的三个参数分别是什么作用?

出题意图: 考察自定义特性的规范写法,有没有实际写过特性。

答:

AttributeUsage 用来约束自定义特性的使用方式,三个参数:

  • AttributeTargets:指定特性能标记在哪里(类、方法、属性、字段等),建议每次自定义特性都明确指定,防止误用。可以用 | 组合多个目标。
  • AllowMultiple:同一位置是否允许重复标记,默认 false。验证框架里一个属性需要同时标 [Required][Length],就需要设为 true
  • Inherited:子类是否继承父类上的特性,默认 true
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class ValidateAttribute : Attribute { }

解答思路: 逐个说清楚,重点举 AllowMultiple 的实际使用场景,体现实战经验。


Q3:特性标记后为什么不能直接调用?怎么让特性生效?

出题意图: 考察对特性运行机制的理解,很多人只会标记特性,不知道背后原理。

答:

特性标记只是在元数据中记录了"这里有一个特性实例",并不会自动执行任何代码。要让特性生效,必须通过反射主动获取特性实例,然后调用其中的方法。

标准流程:先用 IsDefined() 判断是否存在(避免无谓开销),再用 GetCustomAttribute<T>() 获取实例,最后调用实例方法。

Type type = typeof(Student);if (type.IsDefined(typeof(CustomAttribute), true))
{CustomAttribute attr = type.GetCustomAttribute<CustomAttribute>();attr.Do(); // 这里才真正执行特性中的逻辑
}

解答思路: 说清楚"标记 ≠ 执行",反射是让特性生效的唯一途径,顺带提性能优化(先判断再获取)。


Q4:如何用特性+反射实现枚举的描述信息获取?

出题意图: 这是特性"获取额外信息"的经典场景,考察能否把特性用在实际业务中。

答:

定义一个 RemarkAttribute 标记在枚举字段上,通过扩展方法封装反射逻辑,调用方只需 state.GetRemark()

[AttributeUsage(AttributeTargets.Field)]
public class RemarkAttribute : Attribute
{private readonly string _desc;public RemarkAttribute(string desc) => _desc = desc;public string GetRemark() => _desc;
}public enum OrderStatus
{[Remark("待支付")] Pending = 1,[Remark("已完成")] Completed = 2
}public static class EnumExtension
{public static string GetRemark(this Enum @enum){FieldInfo field = @enum.GetType().GetField(@enum.ToString());if (field.IsDefined(typeof(RemarkAttribute), true))return field.GetCustomAttribute<RemarkAttribute>().GetRemark();return @enum.ToString();}
}// 使用
Console.WriteLine(OrderStatus.Pending.GetRemark()); // 待支付

好处:新增枚举值只加一行标记,描述改了只改标记处,获取逻辑完全不动。

解答思路: 先说传统 if-else 的问题,再给出特性方案,最后说明优势。能写出扩展方法封装是加分项。


Q5:请设计一个基于特性的数据验证框架,要求支持扩展新的验证规则

出题意图: 综合考察特性+反射+面向对象设计(开闭原则),是中高级岗位的常见题,考察架构思维。

答:

核心设计:抽象基类定义统一接口,具体验证规则继承实现,验证管理器只依赖抽象基类,新增规则不改已有代码。

// 1. 抽象基类
public abstract class AbstractValidateAttribute : Attribute
{public abstract (bool ok, string error) Validate(object value);
}// 2. 具体规则
[AttributeUsage(AttributeTargets.Property)]
public class RequiredAttribute : AbstractValidateAttribute
{private readonly string _msg;public RequiredAttribute(string msg) => _msg = msg;public override (bool ok, string error) Validate(object value){bool ok = value != null && !string.IsNullOrWhiteSpace(value.ToString());return (ok, ok ? null : _msg);}
}[AttributeUsage(AttributeTargets.Property)]
public class LengthAttribute : AbstractValidateAttribute
{private readonly int _min, _max;public string ErrorMsg { get; set; }public LengthAttribute(int min, int max) { _min = min; _max = max; }public override (bool ok, string error) Validate(object value){if (value == null) return (true, null);int len = value.ToString().Length;bool ok = len >= _min && len <= _max;return (ok, ok ? null : ErrorMsg);}
}// 3. 验证管理器
public static class Validator
{public static (bool ok, string error) Validate<T>(T obj) where T : class{foreach (PropertyInfo prop in typeof(T).GetProperties()){if (!prop.IsDefined(typeof(AbstractValidateAttribute), true)) continue;object value = prop.GetValue(obj);foreach (AbstractValidateAttribute attr in prop.GetCustomAttributes<AbstractValidateAttribute>()){var (ok, error) = attr.Validate(value);if (!ok) return (false, error);}}return (true, null);}
}// 4. 使用
public class UserDto
{[Required("姓名不能为空")]public string Name { get; set; }[Required("手机号不能为空")][Length(11, 11, ErrorMsg = "手机号必须11位")]public string Mobile { get; set; }
}var (ok, error) = Validator.Validate(new UserDto { Name = "张三", Mobile = "123" });
// ok=false, error="手机号必须11位"

扩展新规则只需新建一个继承 AbstractValidateAttribute 的类,Validator 完全不用改。

解答思路: 先说设计思路(抽象基类+开闭原则),再写代码,最后说明扩展方式。能主动提到"不改 Validator 就能扩展"是加分项。


Q6:反射获取特性时,IsDefinedGetCustomAttributes 都能判断特性是否存在,为什么推荐先用 IsDefined

出题意图: 考察对反射性能的关注,体现候选人有没有在意代码质量。

答:

GetCustomAttributes() 会实例化所有特性对象并返回数组,即使你只是想判断"有没有",也会产生对象分配开销。IsDefined() 只做元数据检查,不创建特性实例,性能更好。

在循环中遍历大量属性/方法时,这个差异会被放大:

// 推荐:先判断,有才获取
if (prop.IsDefined(typeof(RequiredAttribute), true))
{var attr = prop.GetCustomAttribute<RequiredAttribute>();attr.Validate(value);
}// 不推荐:每次都创建实例,即使不需要
var attrs = prop.GetCustomAttributes<RequiredAttribute>(true);
if (attrs.Any()) { ... }

解答思路: 说清楚两者的本质区别(是否创建实例),结合循环场景说明影响。


Q7:泛型缓存是什么?在 ORM 中如何用它优化反射性能?

出题意图: 考察对泛型特性的深入理解,以及性能优化意识,中高级岗位必考点。

答:

泛型类的每个类型参数组合都是独立的类,各自有独立的静态字段。利用这一特性,可以把反射结果缓存在静态字段中,同一类型只反射一次。

public class SqlCache<T>
{// 每种 T 对应一份独立的静态字段public static readonly string SelectSql;static SqlCache(){// 静态构造函数只执行一次Type type = typeof(T);var cols = string.Join(",", type.GetProperties().Select(p => $"[{p.Name}]"));SelectSql = $"SELECT {cols} FROM [{type.Name}]";}
}// SqlCache<SysUser>.SelectSql 和 SqlCache<SysCompany>.SelectSql 是两个独立的字段
// 各自只在第一次访问时通过反射生成,之后直接读缓存
string sql = SqlCache<SysUser>.SelectSql + " WHERE Id=1";

对比用 Dictionary<Type, string> 缓存,泛型缓存不需要加锁,线程安全由 CLR 的类型初始化机制保证,性能更好。

解答思路: 先解释泛型类静态字段的独立性,再说为什么比 Dictionary 缓存更好(线程安全、无锁)。


Q8:特性的 Inherited 参数设为 true 时,子类能获取到父类的特性吗?有什么注意事项?

出题意图: 考察对特性继承机制的细节掌握,容易踩坑的地方。

答:

Inherited = true 时,通过 GetCustomAttributes(true) 可以获取到继承链上的特性。但有一个重要细节:接口上的特性不会被实现类继承,只有类上的特性才会被子类继承。

[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public class TagAttribute : Attribute { }[Tag]
public class Base { }public class Child : Base { }// 可以获取到,因为 Child 继承自 Base
bool has = typeof(Child).IsDefined(typeof(TagAttribute), true); // true// 接口上的特性不会被实现类继承
[Tag]
public interface IService { }public class MyService : IService { }
bool hasFromInterface = typeof(MyService).IsDefined(typeof(TagAttribute), true); // false

另外,GetCustomAttributes(false) 只获取当前类型自身的特性,不搜索继承链。

解答思路: 先回答"可以",再说接口特性不继承这个坑,最后说 false/true 参数的区别。

http://www.jsqmd.com/news/501537/

相关文章:

  • 构建千万级用户的高并发抽奖系统架构
  • 美团面试:为什么要用分布式缓存?本地缓存呢?多级缓存一致性如何保证?
  • 深入解析POE交换机:AF与AT标准的技术差异与应用场景
  • 2026七氟丙烷选购攻略:口碑厂商不容错过!,氧气乙炔/氮气/二氧化碳/氩气/混合气/标准气,七氟丙烷生产厂家怎么选择 - 品牌推荐师
  • 基于POI的Luckysheet数据导出优化:解决空指针与格式自动转换问题
  • 揭秘分期乐礼品卡回收流程,团团收全攻略! - 团团收购物卡回收
  • QMCDecode:破解QQ音乐加密格式实现音频自由的高效工具
  • 蓉城筑家,匠心致远——里林设计,解锁成都装修省心新方式 - 推荐官
  • 从伏秒平衡到占空比:BUCK/BOOST电路工作原理图解指南
  • 供水设备多少钱,上海海澄水务产品价格贵吗? - 工业推荐榜
  • TypeScript的override关键字(v4.3+):显式标记方法重写
  • 如何解放双手?OnmyojiAutoScript自动化工具让游戏效率提升300%
  • 【实战指南】STM32F411CEU6 板载 LED 呼吸灯效果实现 —— 从入门到进阶
  • 2026年全国控制柜来样定制厂家排名,这些企业不容错过 - myqiye
  • CVX工具箱安装避坑指南:从下载到运行测试代码的全流程
  • 优化SFTP性能:深入理解MaxSessions与MaxStartups配置
  • 2026 年 3 月 GEO 优化公司榜单:AI 赋能企业增长首选名单 - 速递信息
  • 2026年全国口碑好的小铁自助台球加盟推荐,详细介绍与开店指导揭秘 - mypinpai
  • ATK-IMU601上位机软件数据不更新?可能是排针接反了!详细焊接与接线避坑指南
  • 分期乐礼品卡回收优选平台,团团收让你放心交易! - 团团收购物卡回收
  • Speech Seaco Paraformer语音识别新手指南:单文件、批量、实时录音全解析
  • 03-C#.Net-特性-学习笔记
  • 小铁自助台球开店方案有指导吗,价格多少值得加盟吗 - 工业设备
  • QMCDecode:三步解锁QQ音乐加密格式,让你的音乐真正自由播放
  • 聊聊内蒙古智能印章机信誉好机构怎么选择 - 工业品网
  • 闲置的京东e卡在哪里回收兑换可靠些? - 抖抖收
  • 3行代码实现零成本百度搜索集成:开发者效率提升指南
  • 盘点2026年好用的GEO优化服务商,哪家更适合您的企业 - 工业品牌热点
  • Ostrakon-VL-8B进阶:利用Matlab进行餐饮数据可视化与模型效果分析
  • 2026年口碑好的不锈钢护栏厂商有哪些?一文为你揭晓,比较好的不锈钢护栏厂家选哪家10年质保有保障 - 品牌推荐师