---
id: dependencyproperty
title: 依赖属性
---

### 定义

命名空间：TouchSocket.Core <br/>
程序集：[TouchSocket.Core.dll](https://www.nuget.org/packages/TouchSocket.Core)


## 一、说明
用过WPF的小伙伴一定对依赖属性不陌生。所以TouchSocket模仿其结构，创建了适用于网络框架的依赖属性。


## 二、什么是依赖属性？

我们知道常规属性，就是拥有get，set访问器的字段，叫做属性。
```csharp showLineNumbers
class MyClass
{
    public int MyProperty { get; set; }
}
```
而依赖属性，则是具有注入特征的属性。它具有如下特性：

1. 可以像普通属性一样，声明在类内部（示例1），对外界的公布和常规数据一模一样。
2. 声明在静态类中，做成扩展方法，实现真正的注入型属性（示例2）。
3. 可以声明初始值。

### 2.1 内部声明

1. 继承**DependencyObject**
2. 按如下格式生成属性项（propdp代码块可快速实现）
```csharp showLineNumbers
class MyDependencyObject: DependencyObject
{
    /// <summary>
    /// 属性项
    /// </summary>
    public int MyProperty1
    {
        get { return GetValue(MyPropertyProperty1); }
        set { SetValue(MyPropertyProperty1, value); }
    }

    /// <summary>
    /// 依赖项
	/// </summary>
	public static readonly DependencyProperty<int> MyPropertyProperty1 =
    	DependencyProperty<int>.Register("MyProperty1", typeof(MyDependencyObject), 10);

}
```

### 2.2 扩展声明

扩展声明，必须要提前声明扩展类。

下列示例声明一个MyProperty的属性扩展。

```csharp showLineNumbers
public static class DependencyExtensions
{
   /// <summary>
    /// 依赖项
	/// </summary>
	public static readonly DependencyProperty<int> MyPropertyProperty2 =
    	DependencyProperty<int>.Register("MyProperty2", typeof(DependencyExtensions), 10);

    /// <summary>
    /// 设置MyProperty2
    /// </summary>
    /// <typeparam name="TClient"></typeparam>
    /// <param name="client"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static TClient SetMyProperty2<TClient>(this TClient client, int value) where TClient : IDependencyObject
    {
        client.SetValue(MyPropertyProperty2, value);
        return client;
    }

    /// <summary>
    /// 获取MyProperty2
    /// </summary>
    /// <typeparam name="TClient"></typeparam>
    /// <param name="client"></param>
    /// <returns></returns>
    public static int GetMyProperty2<TClient>(this TClient client) where TClient : IDependencyObject
    {
        return client.GetValue(MyPropertyProperty2);
    }
}
```

那么这时候，**MyDependencyObject**对象即可赋值和获取**MyProperty2**的属性。

```csharp {2-3}
MyDependencyObject obj=new MyDependencyObject();
obj.SetMyProperty2(2);//扩展属性必须通过扩展方法
int value=obj.GetMyProperty2();
```

:::tip 提示

扩展的**SetMyProperty2**和**GetMyProperty2**不是必须的。如果没有这两个方法，我们依然可以使用**GetValue**和**SetValue**方法访问。

```csharp {2-3}
MyDependencyObject obj=new MyDependencyObject();
obj.SetValue(DependencyExtensions.MyPropertyProperty2,2);
int value=obj.GetValue(DependencyExtensions.MyPropertyProperty2);
```

:::  

## 三、场景

假设以下情况：
有一个Person类，已经被封装好了，甚至已经被编译成dll了。但是他只有一个Age属性，如果我们想在开发后期再添加属性，应该怎么办呢？

常规做法就是继承，然后在子类添加属性。亦或者修改源码，重新编译。

无论哪一种都有很大的麻烦事。

继承，会让显式的Person类无法使用声明到子类的属性，到时候必须进行强制转换，而一旦继承分支多起来的话，将非常糟糕。

而重新编译，带来的问题就更大了，总不能把属性都声明在父类吧。何况还有dll版本依赖问题，同事推脱问题，巴拉巴拉。

```csharp showLineNumbers
public class Person
{
    /// <summary>
    /// 年龄
    /// </summary>
    public int Age { get; set; }//常规属性
}
```

那我们如何解决呢？

我们可以在一开始，就将Person类按如下声明。继承DependencyObject。

```csharp showLineNumbers
public class Person : DependencyObject
{
    /// <summary>
    /// 年龄
    /// </summary>
    public int Age { get; set; }//常规属性
}
```

:::tip 提示

如果不方便继承，也可以实现**IDependencyObject**接口，但是可能你需要复制**DependencyObject**的实现源码到你的基类中。

:::  

然后，就Ok了。当后续你需要什么属性的时候，自己声明扩展即可。

这样，你就可以随意的往**Person**类中添加属性了。

```csharp showLineNumbers
public static class DependencyExtensions
{
   /// <summary>
    /// 依赖项
	/// </summary>
	public static readonly DependencyProperty<int> MyPropertyProperty2 =
    	DependencyProperty<int>.Register("MyProperty2", typeof(DependencyExtensions), 10);

    /// <summary>
    /// 设置MyProperty2
    /// </summary>
    /// <typeparam name="TClient"></typeparam>
    /// <param name="client"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static TClient SetMyProperty2<TClient>(this TClient client, int value) where TClient : IDependencyObject
    {
        client.SetValue(MyPropertyProperty2, value);
        return client;
    }

    /// <summary>
    /// 获取MyProperty2
    /// </summary>
    /// <typeparam name="TClient"></typeparam>
    /// <param name="client"></param>
    /// <returns></returns>
    public static int GetMyProperty2<TClient>(this TClient client) where TClient : IDependencyObject
    {
        return client.GetValue(MyPropertyProperty2);
    }
}
```

:::tip 提示

在声明扩展时，还可以对**where TClient : IDependencyObject**进行泛型约束，这样，就能管理属性的作用域了。

:::  

## 四、优缺点

**优点：**

1. 可以不声明在类内部。这意味着可以从外部注入。
2. 不需要初始赋值，也就意味着创建大量对象时，可以不需要占用太多内存。

**缺点：**

1. 对性能有一定性能影响。准确说，对于值类型，有拆装箱、查字典两个损耗。对于引用类型，会多一个字典查询操作。但是这种性能损耗，经实测，在1亿次存取时，才有不到一秒的差距。

