I am looking at Visual Studio Extensibility Samples.
In many samples, I came across this PropertyCollection which I did not understand.
For example, in this IntraTextAdornment example, what is the purpose of the last line in the ColorTaggerProvider.
internal sealed class ColorTaggerProvider : ITaggerProvider
{
public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
{
if (buffer == null)
throw new ArgumentNullException("buffer");
return buffer.Properties.GetOrCreateSingletonProperty(() => new ColorTagger(buffer)) as ITagger<T>;
}
}
Why can't we have directly the following:
return new ColorTagger(buffer) as ITagger<T>;
instead of
return buffer.Properties.GetOrCreateSingletonProperty(() => new ColorTagger(buffer)) as ITagger<T>;
What does this IPropertyOwner represent?
It's written there "provides ownership of an arbitrary set of properties".
So what does ownership here mean? It has a property by name Properties
I find here, "the collection of properties controlled by the property owner."
And the Properties property is of the type PropertyCollection
And it's written "allows property owners to control the lifetimes of the properties in the collection."
And this interface IPropertyOwner is implemented in so many number of class.
I am looking for a bit more explanation in the documentation, but I did not find, am I missing something here?
Here are a few more examples from the samples where I find this Property Owner collection.
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));
}
The point is just to be able to associate a piece of data or object with an ITextBuffer/ITextView. For example, CreateTagger can be called more than once for a buffer or view, so doing GetOrCreateSingletonProperty would mean only one tagger gets created and then shared between all the instances.
You might also find yourself using it for some other state. For example, say you're implementing a language service. You might have a few different features you've implemented, say classification (colorization), and also some IntelliSense completion. In both cases, you need to look at a parse tree or processed representation of the file to provide those features. You might consider putting that shared data into the property bag so that way any of your features can go and find it.
If you didn't have this, the alternative approach would be you'd put data into a Dictionary<ITextBuffer, MyThing>
which unless you're careful means you can easily leak text buffers if you don't remove the entries from the dictionary once the user closes the buffer. Using the property bag makes the lifetime more explicit.
(Those familiar with ConditionalWeakTable might wonder why this property bag pattern exists when you could use that without creating memory leak problems. I think the simple answer is the property bag pattern in the editor predates the existence of ConditionalWeakTable. And of course there's other times a ConditionalWeakTable wouldn't work well anyways.)