JAL Computing
|
Chapter 29 "A Determined Collection Using Generics"Here is some code that demonstrates a deterministic collection of type IDisposable. The collection takes references of type IDisposable using the newed reference idiom. When the collection goes out of using scope the contained IDisposable objects are disposed. Below is the same idiom implemented using Generics! Using a Non Generic CollectionJALCollection is of type IInvoke and IDisposable and you can add objects of type IInvoke and IDisposable to the collection. When the collection goes out of using scope Dispose is invoked on each element in the collection. If Invoke is called on the collection, Invoke is called on each element of the collection. using System; using System.Collections.Generic; using System.Text; using System.Collections; namespace DeterminedCollection { // define interface that Invokes generic method // and releases unmanaged resources in Dispose public interface IUnmanagedWrapper : IDisposable, IInvoke{ } // class that wraps unmanaged resource class DrawWrapper : Disposable, IUnmanagedWrapper { protected override void DisposeManagedResources() { //Console.WriteLine("Disposed Managed Resources."); } protected override void DisposeUnmanagedResources() { Console.WriteLine("Disposed Unmanaged Resources."); } public void Invoke(object o) { if (disposed) { throw new ObjectDisposedException("Wrapper"); } // mimic some type of unmanaged action Console.WriteLine("Draw."); } } class DisposeWrapper : Disposable { protected override void DisposeManagedResources() { //Console.WriteLine("Disposed Managed Resources."); } protected override void DisposeUnmanagedResources() { Console.WriteLine("Disposed Unmanaged Resources."); } } class InvokeWrapper : IInvoke { public void Invoke(object o) { // mimic some type of unmanaged action Console.WriteLine("Invoke"); } } class JALCollection : Disposable, IDisposable { private readonly object syncLock = new Object(); private ArrayList list = new ArrayList(); private object o = null; public JALCollection(object o) { this.o = o; } // ASSERT d is not null // ASSERT no object holds a reference to // d outside of this class // USAGE Add(new MyClass()); ** newed reference idiom ** // where MyClass implements IMyUnmanagedWrapper public int Add(IUnmanagedWrapper d) { int index= -1; if (d != null) { lock (syncLock) { if (disposed) { throw new ObjectDisposedException("JALGenericCollection"); } index= list.Add(d); } } else { throw new ArgumentException(); } return index; } public void Clear() { lock (syncLock) { if (disposed) { throw new ObjectDisposedException("JALGenericCollection"); } foreach (IDisposable d in list) { d.Dispose(); } list.Clear(); } } public void Invoke(object o) { // no one can add or delete during this critical section lock(syncLock) { if (disposed) { throw new ObjectDisposedException("JALCollection"); } foreach(IInvoke d in list) { d.Invoke(o); } } } protected override void DisposeManagedResources() { lock (syncLock) { foreach (IDisposable d in list) { d.Dispose(); } } } protected override void DisposeUnmanagedResources() { // do nothing } } // I got tired of copy and pasting IDisposable // reusable base class public abstract class Disposable : IDisposable { protected bool disposed = false; // subclass needs to implement these two methods abstract protected void DisposeManagedResources(); abstract protected void DisposeUnmanagedResources(); public virtual void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!this.disposed) { if (disposing) // called from Dispose { DisposeManagedResources(); } DisposeUnmanagedResources(); } disposed = true; } ~Disposable() // maps to finalize { Dispose(false); } } // generic method to invoke public interface IInvoke { void Invoke(object o); } // concrete class class PrintWrapper : Disposable, IDisposable, IInvoke { protected override void DisposeManagedResources() { //Console.WriteLine("Disposed Managed Resources."); } protected override void DisposeUnmanagedResources() { Console.WriteLine("Disposed Unmanaged Resources."); } public void Invoke(object o) { if (disposed) { throw new ObjectDisposedException("Wrapper"); } // mimic some type of unmanaged action Console.WriteLine("Print."); } } class Program { static void Main(string[] args) { using (JALCollection jal= new JALCollection(null)) { jal.Add(new DrawWrapper()); jal.Add(new DrawWrapper()); jal.Add(new DrawWrapper()); jal.Invoke(null); jal.Clear(); jal.Add(new DrawWrapper()); jal.Invoke(null); } Console.ReadLine(); } } } Using a Generic CollectionJALGenericCollection is of type (IInvoke<R,P> and IDisposable) and you can add objects of type (IInvoke<R,P> and IDisposable) to the collection. When the collection goes out of using scope Dispose is invoked on each element in the collection. If Invoke<R,P> is called on the collection, Invoke<R,P> is called on each element of the collection where:
using System; using System.Collections.Generic; using System.Text; using System.Drawing; namespace DeterminedGenericCollection { // generic method to invoke public interface IInvoke<R,P> { R Invoke(P p); } public delegate R DInvoke<R,P>(P p); public interface IMyInterface<R,P> : IInvoke<R,P>, IDisposable { } public class TestInvoke<R, P> : IInvoke<R, P> { public delegate R DInvoke(P arg); public DInvoke GetInstanceDelegate() { return new TestInvoke<R,P>.DInvoke(this.Invoke); } public R Invoke(P arg) { System.Console.WriteLine(arg); return default(R); } } // I got tired of copy and pasting IDisposable // reusable base class public abstract class Disposable : IDisposable { protected bool disposed = false; // subclass needs to implement these two methods abstract protected void DisposeManagedResources(); abstract protected void DisposeUnmanagedResources(); public virtual void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!this.disposed) { if (disposing) // called from Dispose { DisposeManagedResources(); } DisposeUnmanagedResources(); } disposed = true; } ~Disposable() // maps to finalize { Dispose(false); } } // concrete class class PrintWrapper : Disposable, IMyInterface<int,Graphics> { protected override void DisposeManagedResources() { //Console.WriteLine("Disposed Managed Resources."); } protected override void DisposeUnmanagedResources() { Console.WriteLine("Disposed Unmanaged Resources."); } public int Invoke(Graphics g) { if (disposed) { throw new ObjectDisposedException("Wrapper"); } // mimic some type of unmanaged action Console.WriteLine("Print: "); return 0; } } // generic collection public class JALGenericCollection<T,R,P> : Disposable, IInvoke<R,P> where T : IDisposable, IInvoke<R,P> { private readonly object syncLock = new object(); private List<T> list = new List<T>(); private R r = default(R); public JALGenericCollection(R r) { this.r = r; } // ASSERT d is not null // ASSERT no object holds a reference to // d outside of this class // USAGE Add(new MyClass()); ** newed reference idiom ** // where MyClass implements IDisposable and IInvoke public void Add(T d) { if (d != null) { lock (syncLock) { if (disposed) { throw new ObjectDisposedException("JALGenericCollection"); } list.Add(d); } } else { throw new ArgumentException(); } } public void Clear() { lock (syncLock) { if (disposed) { throw new ObjectDisposedException("JALGenericCollection"); } foreach (IDisposable d in list) { d.Dispose(); } list.Clear(); } } public R Invoke(P p) { R r = default(R); // no one can add or delete during this critical section lock (syncLock) { if (disposed) { throw new ObjectDisposedException("JALGenericCollection"); } foreach (IInvoke<R,P> i in list) { r= i.Invoke(p); //if (this.r == r) break; // == on generic types NOT ALLOWED! } } return r; } protected override void DisposeManagedResources() { lock (syncLock) { foreach (IDisposable d in list) { d.Dispose(); } } } protected override void DisposeUnmanagedResources() { // do nothing } } class Program { static void Main(string[] args) { using (JALGenericCollection<PrintWrapper,int,Graphics> jalg = new JALGenericCollection<PrintWrapper,int,Graphics>(1)) { Graphics g = null; jalg.Add(new PrintWrapper()); jalg.Add(new PrintWrapper()); jalg.Add(new PrintWrapper()); jalg.Invoke(g); jalg.Clear(); jalg.Add(new PrintWrapper()); jalg.Invoke(g); //jalg.Add(new InvokeWrapper()); // error,not implement IDispose //jalg.Add(new DisposeWrapper()); // error, not implement IInvoke //jalg.Add(new BadWrapper()); // error, not implement IInvoke<int,Graphics> } //jalg.Invoke(); // error, jalg does not exist TestInvoke<bool, int> testInvoke = new TestInvoke<bool, int>(); TestInvoke<bool,int>.DInvoke di= testInvoke.GetInstanceDelegate(); di(3); Console.ReadLine(); } } } The use of generics allows the reuse of the collection since you can create a deterministic collection of any type that implements IDisposable and IInvoke<R,P>. If you pass a type T of PrintWrapper to the generic collection, then you can only add PrintWrapper objects to the collection, creating a homogenous collection. Creating a Heterogeneous CollectionAlternatively, you can create a heterogenous collection of concrete objects that implement IInvoke and IDisposable. To do this, first create a composite interface aptly named IMyInterface as: public interface IMyInterface<R,P> : IInvoke<R,P>, IDisposable { } You can then derive concrete classes that implement IMyInterface as in: class PrintWrapper : Disposable, IMyInterface<int,Graphics> {...} class DrawWrapper : Disposable, IMyInterface<int,Graphics> {...} You can then create a heterogenous collection that contains objects of type IMyInterface: using (JALGenericCollection<IMyInterface<int,Graphics>, int, Graphics> jalg = new JALGenericCollection<IMyInterface<int,Graphics>, int, Graphics>(1)) { Graphics g = null; jalg.Add(new PrintWrapper()); jalg.Add(new DrawWrapper()); jalg.Invoke(g); } Using a Nested DelegateYou may have noticed the nested delegate declared in the TestInvoke class which matches the signature of the Invoke method: public class TestInvoke<R, P> : IInvoke<R, P> { public delegate R DInvoke(P arg); public DInvoke GetInstanceDelegate() { return new TestInvoke<R,P>.DInvoke(this.Invoke); } public R Invoke(P arg) { System.Console.WriteLine(arg); return default(R); } } You can create an instance of the TestInvoke class as: TestInvoke<bool, int> testInvoke = new TestInvoke<bool, int>(); You can then get a concrete implementation of the delegate using the nested delegate as: TestInvoke<bool,int>.DInvoke di= testInvoke.GetInstanceDelegate(); You can then call the testInvoke.Invoke method using the delegate as in: di(1); Which replaces the call: testInvoke.Invoke(1); Note: Returning a concrete implementation of a Delegate is a useful technique since it allows you the twisted coded to code against the type of a specific Delegate. You can declare a interface IMyEvents that defines a GetXXXXHandler method that returns a specific concrete implementation of a Delegate. Any class that implements this interface can return a concrete instance of the Delegate bound to a specific function call in the concrete class that matches the Delegate signature. This allows you the twisted coder, the ability to fire an event to any class that implements IMyEvents knowing that the class will provide an implementation that matches the Delegate signature. The twisted coder programs to a type (in this case a type defined by a Delegate) unconcerned about the implementation of the type. The caller does not know the class of the object, only that the object implements the type. Have fun! |
Send mail to [email protected]
with questions or comments about this web site. Copyright © 2001, 2002, 2003,
2004, 2005, 2006, 2007, 2008, 2009 ©
|