C#中的CLR属性、依赖属性与附加属性

news2025/6/12 2:04:37
  • CLR属性的主要特征

  1. 封装性

    • 隐藏字段的实现细节

    • 提供对字段的受控访问

  2. 访问控制

    • 可单独设置get/set访问器的可见性

    • 可创建只读或只写属性

  3. 计算属性

    • 可以在getter中执行计算逻辑

    • 不需要直接对应一个字段

  4. 验证逻辑

    • 可以在setter中添加值验证

    • 可以抛出异常拒绝无效值

  5. 通知机制

    • 可以手动实现属性变更通知(如INotifyPropertyChanged)

  6. 线程安全

    • 可以添加线程同步逻辑

CLR属性的实现原理

基本实现

CLR属性本质上是编译器生成的"语法糖",编译后会转换为方法调用:

// 源代码
public class Person
{
    private string _name;
    
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

// 编译后相当于
public class Person
{
    private string _name;
    
    public string get_Name()
    {
        return this._name;
    }
    
    public void set_Name(string value)
    {
        this._name = value;
    }
}

自动实现属性

C# 3.0引入的自动属性进一步简化了语法:

public string Name { get; set; }

编译器会自动生成一个隐藏的私有字段(通常以<Name>k__BackingField命名)和对应的get/set方法。

属性元数据

在IL(中间语言)层面,属性是通过以下元数据表示的:

  1. Property表:记录属性名称、类型和访问器方法

  2. Method表:存储get/set方法实现

  3. Field表:对于自动属性,存储编译器生成的私有字段

属性访问性能

属性访问的性能与方法调用相当,因为:

  1. 简单属性(get;set;)通常会被JIT内联优化

  2. 复杂属性(包含逻辑的)与方法调用开销相同

  3. 虚属性(virtual)会有额外的虚方法调用开销

与依赖属性的比较

特性CLR属性依赖属性
存储直接存储在对象中存储在DependencyObject的全局字典中
绑定支持需实现INotifyPropertyChanged原生支持
动画支持不支持原生支持
默认值需在构造函数设置可通过元数据指定
继承不支持支持属性值继承
内存占用每个实例都有存储只有修改过的值才占用内存
适用场景普通业务对象WPF/Silverlight/UWP控件

CLR属性的高级用法

  1. 索引器

public string this[int index] { get { /*...*/ } set { /*...*/ } }
  1. 表达式体属性(C# 6+):

public string FullName => $"{FirstName} {LastName}";
  1. 初始化器(C# 6+):

public string Name { get; set; } = "Anonymous";
  1. 只读自动属性(C# 6+):

public string Id { get; } = Guid.NewGuid().ToString();

CLR属性是C#面向对象编程的基础设施,提供了字段访问的抽象层,既能保持简洁的语法,又能提供灵活的控制逻辑。

  • 依赖属性与附加属性

依赖属性(Dependency Property)

实现原理

依赖属性是WPF/Silverlight/UWP等XAML技术中的核心概念,它扩展了传统的CLR属性,提供了更丰富的功能:

  1. 属性值继承:子元素可以继承父元素的属性值

  2. 数据绑定支持:可以直接作为数据绑定的目标

  3. 动画支持:可以被动画系统直接操作

  4. 样式支持:可以通过样式设置

  5. 元数据支持:可以指定默认值、验证回调等

  6. 值优先级系统:多个值源按照优先级决定最终值

实现依赖属性的关键是通过DependencyProperty类和DependencyObject基类:

public class MyControl : DependencyObject
{
    // 注册依赖属性
    public static readonly DependencyProperty MyPropertyProperty = 
        DependencyProperty.Register(
            "MyProperty",                     // 属性名称
            typeof(string),                  // 属性类型
            typeof(MyControl),               // 拥有者类型
            new PropertyMetadata("默认值"));  // 元数据
    
    // CLR包装器
    public string MyProperty
    {
        get { return (string)GetValue(MyPropertyProperty); }
        set { SetValue(MyPropertyProperty, value); }
    }
}

应用场景

  1. 自定义控件开发:为自定义控件添加可绑定、可样式化的属性

  2. 数据绑定:作为数据绑定的目标属性

  3. 动画:创建可动画化的属性

  4. 模板绑定:在控件模板中使用TemplateBinding

  5. 样式设置:通过样式设置多个控件的属性值

附加属性(Attached Property)

实现原理

附加属性是一种特殊的依赖属性,它允许一个类为其他类定义属性,常用于布局系统和服务模式:

public class GridHelper
{
    // 注册附加属性
    public static readonly DependencyProperty RowCountProperty =
        DependencyProperty.RegisterAttached(
            "RowCount",                     // 属性名称
            typeof(int),                     // 属性类型
            typeof(GridHelper),              // 拥有者类型
            new PropertyMetadata(1, OnRowCountChanged)); // 元数据
    
    // Get访问器(必须为public static)
    public static int GetRowCount(DependencyObject obj)
    {
        return (int)obj.GetValue(RowCountProperty);
    }
    
    // Set访问器(必须为public static)
    public static void SetRowCount(DependencyObject obj, int value)
    {
        obj.SetValue(RowCountProperty, value);
    }
    
    // 属性变更回调
    private static void OnRowCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is Grid grid)
        {
            // 当RowCount变化时,调整Grid的行定义
            grid.RowDefinitions.Clear();
            for (int i = 0; i < (int)e.NewValue; i++)
            {
                grid.RowDefinitions.Add(new RowDefinition());
            }
        }
    }
}

应用场景

  1. 布局系统:如Grid.Row、Grid.Column等

  2. 服务模式:如ToolTipService.ToolTip、ScrollViewer.IsScrollable等

  3. 行为扩展:为现有控件添加额外功能

  4. 自定义布局面板:创建自己的布局容器时定义布局属性

两者比较

特性依赖属性附加属性
定义方式在定义类中使用在任何类中定义,可附加到其他对象
注册方法RegisterRegisterAttached
访问器实例属性静态方法
典型用途为类定义标准属性为其他类扩展属性

高级主题

  1. 属性值优先级:本地值 > 动画 > 本地样式 > 触发器 > 隐式样式 > 样式触发器 > 模板触发器 > 样式Setter > 默认值

  2. 属性变更回调:通过PropertyMetadata指定属性变化时的处理逻辑

  3. 验证回调:通过ValidateValueCallback进行值验证

  4. 强制回调:通过CoerceValueCallback强制属性值在特定范围内

依赖属性和附加属性是WPF等XAML技术的核心机制,理解它们的原理和用法对于开发复杂的XAML应用程序至关重要。

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

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

相关文章

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期&#xff0c;让读者对此有深刻印象。 目录 ​…

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…

基于IDIG-GAN的小样本电机轴承故障诊断

目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) ​梯度归一化(Gradient Normalization)​​ (2) ​判别器梯度间隙正则化(Discriminator Gradient Gap Regularization)​​ (3) ​自注意力机制(Self-Attention)​​ 3. 完整损失函数 二…

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…