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

CLR属性的主要特征

  1. 封装性

    • 隐藏字段的实现细节

    • 提供对字段的受控访问

  2. 访问控制

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

    • 可创建只读或只写属性

  3. 计算属性

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

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

  4. 验证逻辑

    • 可以在setter中添加值验证

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

  5. 通知机制

    • 可以手动实现属性变更通知(如INotifyPropertyChanged)
  6. 线程安全

    • 可以添加线程同步逻辑

CLR属性的实现原理

基本实现

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

cs 复制代码
// 源代码
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引入的自动属性进一步简化了语法:

cs 复制代码
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. 索引器
cs 复制代码
public string this[int index] { get { /*...*/ } set { /*...*/ } }
  1. 表达式体属性(C# 6+):
cs 复制代码
public string FullName => $"{FirstName} {LastName}";
  1. 初始化器(C# 6+):
cs 复制代码
public string Name { get; set; } = "Anonymous";
  1. 只读自动属性(C# 6+):
cs 复制代码
public string Id { get; } = Guid.NewGuid().ToString();

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

  • 依赖属性与附加属性

依赖属性(Dependency Property)

实现原理

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

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

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

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

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

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

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

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

cs 复制代码
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)

实现原理

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

cs 复制代码
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. 自定义布局面板:创建自己的布局容器时定义布局属性

两者比较

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

高级主题

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

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

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

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

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

相关推荐
htj104 分钟前
C# 使用正则表达式
正则表达式·c#
~plus~4 分钟前
WPF八大法则:告别模态窗口卡顿
开发语言·经验分享·后端·程序人生·c#
就是有点傻13 分钟前
使用WPF的Microsoft.Xaml.Behaviors.Wpf中通用 UI 元素事件
c#
qq_297908011 小时前
C#报价系统陈列展示成本核算系统项目管理系统纸品非纸品报价软件
sqlserver·c#·.net·开源软件
IGP915 小时前
20250606-C#知识:委托和事件
开发语言·c#
Kookoos15 小时前
ABP VNext 与 Neo4j:构建基于图数据库的高效关系查询
数据库·c#·.net·neo4j·abp vnext
张鱼小丸子_微辣16 小时前
.Net Framework 4/C# LINQ*
c#
..活宝..18 小时前
【Emgu CV教程】11.2、Scharr边缘检测
图像处理·计算机视觉·c#·emgu cv·图像分析
yngsqq18 小时前
事件监听 ——CAD C#二次开发
c#