关于Microsoft.VisualStudio.Utilities.PropertyCollection的技术疑问
问题1
在IntraTextAdornment示例的ColorTaggerProvider最后一行代码中,使用buffer.Properties.GetOrCreateSingletonProperty(() => new ColorTagger(buffer)) as ITagger<T>的目的是什么?为何不能直接返回new ColorTagger(buffer) as ITagger<T>?
解答
核心目的是保证每个文本缓冲区(buffer)对应唯一的ColorTagger实例。
如果直接返回new ColorTagger(buffer),每次编辑器框架请求Tagger时都会创建新实例,会引发两个问题:
- 资源浪费:多个Tagger监听同一缓冲区的文本变更,重复执行标记逻辑,消耗不必要的计算资源;
- 状态不一致:不同Tagger实例可能持有不同的状态,导致文本标记显示混乱。
GetOrCreateSingletonProperty的逻辑是:先检查当前buffer的Properties集合中是否已存在对应的Tagger实例,存在则直接返回,不存在才通过委托创建新实例并缓存到集合中,确保针对当前buffer的Tagger全局唯一。
问题2
IPropertyOwner代表什么?文档提到它“提供任意属性集的所有权”,这里的“所有权”具体指什么?其Properties属性(类型为PropertyCollection)的作用是什么,为何说它允许属性所有者控制集合中属性的生命周期?
解答
IPropertyOwner是Visual Studio扩展性框架中的核心接口,代表拥有一组自定义属性的对象。
这里的“所有权”包含两层意思:
- 归属权:Properties集合属于该对象,所有属性都与这个对象绑定,是对象的附加数据;
- 控制权:对象本身负责管理Properties集合中属性的生命周期,包括添加、移除,以及对象销毁时的自动清理。
Properties属性的作用是给对象动态附加任意自定义数据,相当于一个“动态属性容器”——不需要修改对象的类定义,就能给不同的IPropertyOwner对象(比如文本缓冲区、编辑器视图、编辑器项)添加业务相关的自定义数据。
它能控制生命周期的原因:
- 当IPropertyOwner对象被销毁时,其Properties集合会被一并回收,集合中的属性实例也会被GC处理;
- 对象可以主动调用
RemoveProperty清理不再需要的属性,提前释放资源,避免内存泄漏。
问题3
对示例中PropertyCollection的几种用法也有疑惑:
- 添加属性:
item.Properties.AddProperty(nameof(ElementCatalog.Element), element); - 获取属性:
item.Properties.TryGetProperty<ElementCatalog.Element>(nameof(ElementCatalog.Element), out var matchingElement) - 移除属性:
public override void Dispose() { base.view.Properties.RemoveProperty(typeof(ColorAdornmentTagger)); }
解答
这三种是PropertyCollection的标准增、查、删操作,各自的作用如下:
- 添加属性:
AddProperty给当前IPropertyOwner对象附加一组键值对,用nameof(ElementCatalog.Element)作为唯一键,确保不会和其他属性冲突,值为业务对象element——相当于给对象绑定自定义数据标签。 - 获取属性:
TryGetProperty是安全读取属性的方式,若键对应的属性存在,就将值输出到matchingElement并返回true;若不存在则返回false,不会抛出空引用异常,适合不确定属性是否存在的场景。 - 移除属性:在
Dispose方法中调用RemoveProperty,是因为ColorAdornmentTagger是附加在view上的资源,当view被销毁时,主动移除对应的属性能确保Tagger实例被GC回收,避免内存泄漏。这里用typeof(ColorAdornmentTagger)作为键,是因为实例创建时大概率是以类型为标识缓存到Properties中的。
内容的提问来源于stack exchange,提问作者VivekDev

