Skip to content

Commit c0defeb

Browse files
committed
Wiki docs from CodePlex, updated for v4.
1 parent 65484a3 commit c0defeb

13 files changed

+161
-0
lines changed

doc/README.md

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Overview
2+
3+
Nito.ConnectedProperties provides an API for attaching properties to most objects at runtime.
4+
5+
A _connectible property_ is a piece of data that may be connected to a _carrier object_ at runtime. A connectible property is either _connected_ or _disconnected_. Only properties that are connected have values.
6+
7+
**Note:** In the documentation and IntelliSense, the term _connected property_ is often used as a synonym for _connectible property_. The term _connected property_ does not imply that the connectible property is actually _connected_. If there is any possibility of confusion, the term _connectible property_ is used instead of _connected property_.
8+
9+
A _carrier object_ is any instance capable of having properties connected to it - specifically, a reference type that uses reference equality.
10+
11+
A _connected property scope_ manages the connections between connectible properties and carrier objects. There is a default scope that is sufficient for most people, but you can also create your own connected property scope instance.
12+
13+
All accesses to connectible property values are thread-safe. However, the actual _value_ of the connected property is only thread-safe if the connected property type is thread-safe (e.g., an immutable type).
14+
15+
# Table of Contents
16+
17+
[Connected Property Values](connected-property-values.md)
18+
19+
[Carrier Objects](carrier-objects.md)
20+
21+
[Connected Property Scopes](scope.md)
22+
23+
[Object Lifetimes](object-lifetimes.md)
24+
25+
[Limitations](limitations.md)
26+
27+
Techniques
28+
- [Extension Method Wrappers](extension-method-wrappers.md)
29+
- [Interfaces and Inheritance](interfaces-and-inheritance.md)
30+
- [Connected Methods](connected-methods.md)
31+
32+
Details
33+
- [AppDomains](app-domains.md)
34+
- [Notes on Naming](notes-on-naming.md)
35+
- [Library Compatibility](library-compatibility.md)
36+
- [How It Works](how-it-works.md)

doc/app-domains.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The default connected property [scope](scope.md) instance is _per-AppDomain_. This means that the same names used in different AppDomains refer to different properties.
2+
3+
When an AppDomain is unloaded, all property values connected by the default connected property scope in that AppDomain become eligible for garbage collection.

doc/carrier-objects.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
A carrier object is an object that may have properties connected to it.
2+
3+
Carrier objects must be reference types; value types cannot have properties connected to them.
4+
5+
Furthermore, carrier objects must use reference equality. Types such as `String` do not qualify as carrier objects. This restriction is checked at runtime, and the Connected Properties library will throw an `InvalidOperationException` if you attempt to connect a property to an invalid carrier object.
6+
7+
As a result of these restrictions, two different carrier object variables have the same connected property instances iff the two objects are equal (i.e., both objects are the same instance).
8+
9+
In advanced scenarios, you may need to connect properties to a carrier object that does not use reference equality. You may bypass carrier object validation by passing a value of `true` for the `bypassValidation` argument.

doc/connected-methods.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Connected properties only allow attaching _data_ to carrier objects, but you may attach a delegate as data. It can then be used as a connected method.
2+
3+
This is similar to setting delegates on an ExpandoObject.
4+
5+
````C#
6+
private const string LogMethod = Guid.NewGuid().ToString("N");
7+
8+
public void Log(object obj)
9+
{
10+
var logMethodProperty = ConnectedProperty.GetConnectedProperty(obj, LogMethod);
11+
Action logMethod;
12+
if (logMethodProperty.TryGet(out logMethod))
13+
{
14+
// This instance has a connected Log method.
15+
logMethod();
16+
}
17+
else
18+
{
19+
// This instance does not have a connected Log method.
20+
GenericLog();
21+
}
22+
}
23+
````
24+
25+
Note that connected methods are different than extension methods. Extension methods extend a _type_, and can be called for any instance of that type. Connected methods extend an _instance_, so they can be overridden on a per-instance basis.

doc/connected-property-values.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
A connectible property is either _connected_ or _disconnected_.
2+
3+
When a property is connected to a carrier object, it has a value related to that object.
4+
5+
**Note:** A disconnected property is different than a connected property with a value of `null`.
6+
7+
Connected properties expose [an API](http://dotnetapis.com/pkg/Nito.ConnectedProperties/4.0.0-eta-01/netstandard1.1/doc/Nito.ConnectedProperties.ConnectedProperty) that allows the full range of connecting, disconnecting, getting, setting, and updating, all in a fully threadsafe fashion.

doc/extension-method-wrappers.md

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
The syntax for directly using connected properties is a bit awkward. One way to make it less awkward for end-users is to wrap connected property access into extension methods, as such:
2+
3+
````C#
4+
// Represents a piece of data that has been read out of a file. We want to connect a property containing the source line number.
5+
public class Token { .. }
6+
7+
// Allow access to the connected property via extension methods.
8+
using Nito.ConnectedProperties;
9+
public static class TokenExtensions
10+
{
11+
private const string SourceLineNumber = Guid.NewGuid().ToString("N");
12+
13+
public static int GetSourceLineNumber(Token token) => ConnectedProperty.Get(token, SourceLineNumber);
14+
15+
public static void SetSourceLineNumber(Token token, int lineNumber) => ConnectedProperty.Set(token, SourceLineNumber, lineNumber);
16+
}
17+
````
18+
19+
The end-user then has `GetSourceLineNumber` and `SetSourceLineNumber` extension methods that they may use, instead of having to use the Connected Properties library directly.

doc/how-it-works.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
The Connected Properties library is just a thin wrapper around [`ConditionalWeakTable<TKey, TValue>`](http://msdn.microsoft.com/en-us/library/dd287757.aspx).
2+
3+
Each "connected property scope" is actually an instance of `ConditionalWeakTable`.
4+
5+
The wrapper provides the following advantages over using `ConditionalWeakTable` directly:
6+
7+
* You may attach value types to carrier objects (`ConditionalWeakTable` values must be reference types).
8+
* Carrier objects are checked at runtime to prevent accidentally using an improper carrier object.
9+
* The API for accessing property values is more complete (e.g., `Set`) and does not have [vexing exceptions](https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/) (`ConditionalWeakTable` has a vexing exception for its `Add` method).

doc/interfaces-and-inheritance.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Care must be taken when connecting properties to carrier objects through interfaces or base classes.
2+
3+
Interfaces may be implemented by reference types or value types, and value types cannot be carrier objects. Similarly, a reference-equatable base class may have a derived class that uses value equality, and an instance of that derived class cannot be a carrier object.
4+
5+
The `TryGetConnectedProperty` method may be used to access properties only if the carrier instance is a valid carrier:
6+
7+
````C#
8+
public void DisplayName(object obj)
9+
{
10+
var nameProperty = ConnectedPropertyScope.Default.TryGetConnectedProperty(obj, "Name");
11+
if (nameProperty != null)
12+
{
13+
Console.WriteLine("Name: " + nameProperty.Get());
14+
}
15+
else
16+
{
17+
Console.WriteLine("No name can be connected to this kind of carrier object.");
18+
}
19+
}
20+
````

doc/library-compatibility.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
The ConnectedProperties library:
2+
- uses [semantic versioning](http://semver.org/), and has used it from day one.
3+
- is available as a NuGet package for installation from within Visual Studio.
4+
- supports [netstandard](https://github.com/dotnet/standard).

doc/limitations.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Limited Enumeration
2+
3+
It is _not_ possible to enumerate _all_ properties connected to a given carrier object by all [connected property scopes](scope.md).
4+
5+
It is _not_ possible to enumerate all carrier objects for a given [connected property scope](scope.md).
6+
7+
# Not a GC Callback
8+
9+
Since connected property values are not disposed, they _cannot_ be used as an "object has been garbage collected" type of callback.

doc/notes-on-naming.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"Connected properties" should really be called "attached properties", but that name [was already taken by WPF](http://msdn.microsoft.com/en-us/library/ms749011.aspx).
2+
3+
Likewise, the term "bound properties" was considered (in the sense that the properties "bind" to their carrier objects), but that term [was already associated with data binding](http://en.wikipedia.org/wiki/Bound_property).
4+
5+
So, I settled on the term "connected properties".

doc/object-lifetimes.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Connected properties do not extend the lifetime of their carrier objects in any way. This is true even if the property value references its carrier object.
2+
3+
A property that is connected to a carrier object will become eligible for garbage collection when _any_ of the following conditions take place:
4+
- The carrier object becomes eligible for garbage collection. When a carrier object is garbage collected, all of its connected property values become eligible for garbage collection.
5+
- The property is disconnected. If the property is explicitly disconnected, then its former value will become eligible for garbage collection immediately.
6+
- The connected property scope becomes eligible for garbage collection. When a scope is garbage collected, all of its property values that are connected to any carrier objects become eligible for garbage collection (however, see the note below).
7+
**Note:** This last point only applies to connected property scopes that you explicitly create. The default connected property scope is only garbage collected when the AppDomain shuts down.
8+
(This is assuming that there are no other references to the property value; if there are, then of course it does not become eligible for garbage collection.)
9+
10+
Note that if you have a property value that references a scope (even its own scope), then the lifetime of the scope is extended to that of the property value.
11+
12+
Connected property values are never disposed, but they will be finalized.

doc/scope.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
There is a single default connected property scope for the application domain, `ConnectedPropertyScope.Default`. You should use this one unless you need to create one of your own.
2+
3+
Every scope is completely independent from every other scope. Each scope manages its own set of connected properties.

0 commit comments

Comments
 (0)