How to create api for persistent collection in c #?

I am going to create a permanent collection (lists or another) in C #, but I can not understand a good API.

I use persistent in the Clojure sense : a constant list is a list that behaves as if it has value semantics instead of referential semantics, but does not bear the overhead of copying large value types. Persistent collections use copy-on-write to share internal structure. Pseudocode:

l1 = PersistentList()
l1.add("foo")
l1.add("bar")
l2 = l1
l1.add("baz")

print(l1) # ==> ["foo", "bar", "baz"]
print(l2) # ==> ["foo", "bar"]
# l1 and l2 share a common structure of ["foo", "bar"] to save memory

Clojure uses such data structures, but in addition in Clojure all data structures are immutable. There is some overhead in performing all copy-to-write operations, so Clojure provides a workaround in the form of transient datastructures that you can use if you are sure that you are not using anyone else. If you have a single link to a data structure, why not reinstall it directly, instead of going through all the copying overhead.

( , Clojure ). refcount 1, , . , - , , -- .

API refcounting, API , , , COW'ed, API , , COW .

# structs, . , , incref()/decf() .

- #, API?

:

  • , API. Clojure Java.
  • , , , COW'ed . refcounting , COWing, , -, API.
+5
4

, , , . , , , , .

, . , , Clojure, # ( - , - ).

+3

WeakReference , . WeakReference, . WeakReference , , .

3: , #. . .

2: . @AdamRobinson: , , WeakReference . , , , , , . . , . , , .

class Program
{

  static void Main(string[] args)
  {
    var l1 = default(COWList);
    l1.Add("foo"); // initialize
    l1.Add("bar"); // no copy
    l1.Add("baz"); // no copy
    var l2 = l1;
    l1.RemoveAt(0); // copy
    l2.Add("foobar"); // no copy
    l1.Add("barfoo"); // no copy
    l2.RemoveAt(1); // no copy
    var l3 = l2;
    l3.RemoveAt(1); // copy
    Trace.WriteLine(l1.ToString()); //  bar baz barfoo
    Trace.WriteLine(l2.ToString()); // foo baz foobar
    Trace.WriteLine(l3.ToString()); // foo foobar
  }
}

struct COWList
{
  List<string> theList; // Contains the actual data
  object dummy; // helper variable to facilitate detection of copies of this struct instance.
  WeakReference weakDummy; // helper variable to facilitate detection of copies of this struct instance.

  /// <summary>
  /// Check whether this COWList has already been constructed properly.  
  /// </summary>
  /// <returns>true when this COWList has already been initialized.</returns>
  bool EnsureInitialization()
  {
    if (theList == null)
    {
      theList = new List<string>();
      dummy = new object();
      weakDummy = new WeakReference(dummy);
      return false;
    }
    else
    {
      return true;
    }
  }

  void EnsureUniqueness()
  {
    if (EnsureInitialization())
    {

      // If the COWList has been copied, removing the 'dummy' reference will not kill weakDummy because the copy retains a reference.
      dummy = new object();

      GC.Collect(2); // OUCH! This is expensive. You may replace it with GC.Collect(0), but that will cause spurious Copy-On-Write behaviour.
      if (weakDummy.IsAlive) // I don't know if the GC guarantees detection of all GC'able objects, so there might be cases in which the weakDummy is still considered to be alive.
      {
        // At this point there is probably a copy.
        // To be safe, do the expensive Copy-On-Write
        theList = new List<string>(theList);
        // Prepare for the next modification
        weakDummy = new WeakReference(dummy);
        Trace.WriteLine("Made copy.");

      }
      else
      {
        // At this point it is guaranteed there is no copy.
        weakDummy.Target = dummy;
        Trace.WriteLine("No copy made.");

      }
    }
    else
    {

      Trace.WriteLine("Initialized an instance.");

    }
  }

  public void Add(string val)
  {
    EnsureUniqueness();
    theList.Add(val);
  }

  public void RemoveAt(int index)
  {
    EnsureUniqueness();
    theList.RemoveAt(index);
  }

  public override string ToString()
  {
    if (theList == null)
    {
      return "Uninitialized COWList";
    }
    else
    {
      var sb = new StringBuilder("[ ");
      foreach (var item in theList)
      {
        sb.Append("\"").Append(item).Append("\" ");
      }
      sb.Append("]");
      return sb.ToString();
    }
  }
}

:

Initialized an instance.
No copy made.
No copy made.
Made copy.
No copy made.
No copy made.
No copy made.
Made copy.
[ "bar" "baz" "barfoo" ]
[ "foo" "baz" "foobar" ]
[ "foo" "foobar" ]
+1

, , API "-".

, , ""; , . Get Set, , , ReaderWriterLock, , , , - .

factory , ""; ( ). , singleton. , "", "", - .

, , . , , L1 L2 A, L3 A, . L3 A, L1 L2. , , , , , .NET.

0

- , - ( .net), "" node . , , node :

  • SharedImmutable
  • UnsharedMutable

Calling Clone() sharedImmutable node ; Clone Flexible node SharedImmutable. Calling Clone unshared mutable node node ; .

, UnsharedMutable. UnsharedMutable, , (node, ) UnsharedMutable (). , SharedImmutable, ( ForceClone) , . , UnsharedMutable.

.

MyCollection["this"]["that"]["theOther"].Add("George")
should be evaluated if indexing operations return an indexer class that contains a reference to MyCollection. At this point, the Add method can then act on any intermediate nodes that it had to perform in order to perform any necessary copy-to-write operations.
0
source

All Articles