Enhanced C#
Language of your choice: library documentation
List of all members
Loyc.Utilities.GoDecoratorFieldAttribute Class Reference

This attribute marks a field in an abstract class as pointing to a wrapped object to which GoInterface should forward calls. It is used when you want GoInterface to "complete" a decorator pattern for you. More...

Source file:
Inheritance diagram for Loyc.Utilities.GoDecoratorFieldAttribute:


This attribute marks a field in an abstract class as pointing to a wrapped object to which GoInterface should forward calls. It is used when you want GoInterface to "complete" a decorator pattern for you.

After writing the basic functionality of GoInterface, I realized it could also serve as a handy way to implement the Decorator pattern. A decorator is a class that wraps around some target class (usually sharing the same interface or base class), while modifying the functionality of the target. For instance, you could write a decorator for TextWriter that filters out curse words, replacing them with asterisks.

Writing decorators is sometimes inconvenient because you only want to modify the behavior of some functions while leaving others alone. Without GoInterface, you must write a wrapper for every method, manually forwarding calls from the decorator to the target.

GoInterface can help by generating forwarding functions automatically.

The example shows how to use GoInterface to help you make a decorator.

// A view of an IList in which the order of the elements is reversed. // The test suite offers this example in full; this partial implementation // just explains the concepts. public abstract class ReverseView<T> : IList<T> { // Use the GoDecoratorField attribute so that GoInterface will access // the list through this field instead of creating a new field. // Important: the field must be "protected" or "public" and have // exactly the right data type; otherwise, GoInterface will ignore // it and create its own field in the generated class. [GoDecoratorField] protected IList<T> _list;

// The derived class will init _list for you if you have a default // constructor. If your constructor instead takes an IList argument, // you are expected to initialize _list yourself. protected ReverseView() { Debug.Assert(_list != null); }

// The downside of using GoInterface to help you make decorators is // that GoInterface creates a derived class that overrides abstract // methods in your own class, which means your class must be abstract, // and users can't write "new ReverseView"–instead you must provide // a static method like this one to create the wrapper. public static ReverseView<T> From(IList<T> list) { return GoInterface<ReverseView<T>, IList<T>>.From(list); }

// Here are two of several methods whose functionality we need to // modify in order to reverse a list. public int IndexOf(T item) { int i = _list.IndexOf(item); return i == -1 ? -1 : Count - 1 - i; } public void Insert(int index, T item) { _list.Insert(Count - index, item); }

// Here are the functions that we don't have to implement, which we // allow GoInterface to implement automatically. Unfortunately, when // implementing an interface you can't simply leave out the functions // you want to remain abstract. C#, at least, requires you to make a // list of the interface methods that you don't want to implement. // This inconvenience is only when implementing an interface; if you // are just deriving from an abstract base class, you don't have to // do this because the base class already did it. public abstract void Add(T item); public abstract void Clear(); public abstract bool Contains(T item); public abstract void CopyTo(T[] array, int arrayIndex); public abstract int Count { get; } public abstract bool IsReadOnly { get; } public abstract bool Remove(T item); public abstract IEnumerator<T> GetEnumerator();

// IEnumerable has two GetEnumerator functions so you must use an // "explicit interface implementation" for the second one. // You must write this one yourself, as it can't be marked abstract. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } }