Click here to Skip to main content
Click here to Skip to main content
Go to top

GOF's Adapters and Some Magic

, 21 Apr 2009
Rate this:
Please Sign up or sign in to vote.
How to create automated Adapters and extend/modify the original functionality with default member’s implementation for interfaces, Eiffel-like Separates, Code Injection, and Design by Contract.

Introduction

GOF’s Adapters provide a clean and easy way to create compatible interfaces for existent hierarchies. This work shows how to create automated Adapters and extend/modify original functionality with the default member’s implementation for interfaces, Eiffel-like Separates, Code Injection, and Design by Contract.

Background

Some time ago, when I used to work with Windows and Web front-ended database applications, I realized the lack of a single interface for data-binding operations. I started creating my own way to use both front ends with a single interface set. Later when I published my work here at “The Code Project”, someone told me there was already a GOF’s Design Pattern to do exactly the same. Rediscovering the wheel wasn’t that bad in the end. Now, I want to combine a couple of these GOF’s patterns and some other techniques to create some of the same and some more.

You'll find in this document

  1. Automatic Adapters
  2. Default Member Implementations for Interfaces.
  3. Eiffel-like Separates
  4. Injections
  5. Design by Contract

About this Work

This is not a product, it just has some ideas I came up with and would like to share with this community. This work is not near to be finished. Using the code could generate exceptions, although samples do work. Automatic Adapters work perfectly as far as I could test. Design by Contract is the most incomplete part here, it's been included just to show how much of it could be achieved.

GOF's Adapters

Adapters are used to translate incompatible instances into a single compatible interface. Initially, this work was just about that [1]. This is the pattern I’ll be using. It isn't really the definition for an adapter, because in my case, adaptees have the same "interface" although they don't implement the same "interface"; but I guess it's close enough to be understood.

Let's take, for example, the following interface: IAdapter. It has every kind of member an interface can define (if I'm wrong, please… quickly, let me know).

public interface IAdapter
{
    int this[int index]
    {
        get;
          set;
    }
    int X 
    {
        get;
        set;
    }
    void Method(int x);
    event EventHandler Event1;
}
Excerpt 1: An adapter, one that has everything.

Let's take a look at a couple of classes that could be "adapted" to this IAdapter interface.

public class Adaptee1
{
       int[] lst_Numbers = new int[16];
    public int this[int index]
    {
          get { return lst_Numbers[index]; }
          set { lst_Numbers[index] = value; }
    }

    int int_X ;
    public int X
    {
          get { return int_X; }
          set { int_X = value; }
    }

    public event EventHandler Event1;
    public void Method(int x) 
     { 
         Console.WriteLine("Adaptee1::F({0})", x); 
     }

    public void OnEvent1()
    {
          if (Event1 != null)
        Event1(this, EventArgs.Empty);
    }
}

public class Adaptee2
{
    int[] lst_Numbers = new int[16];
    public int this[int index]
    {
          get { return lst_Numbers[index]; }
          set { lst_Numbers[index] = value; }
    }

    int int_X;
    public int X
    {
          get { return int_X; }
          set { int_X = value; }
    }

    public event EventHandler Event1;
    public void Method(int x) 
    {
          Console.WriteLine("Adaptee2::F({0})", x); 
    }

    public void OnEvent1()
    {
          if (Event1 != null) 
    Event1(this, EventArgs.Empty);
    }
}
Excerpt 2: Two perfectly adjusted adaptees

Both implementations are exactly the same…Yeah I know, examples usually are. I promise I'll come up with something better for the next one. Their "interfaces" are exactly the same, but as they both lack the same base class or a common interface implementation, they cannot be used polymorphically as if they belong in the same hierarchy. We can build an adapter implementing IAdapter for each adaptee and the problem is solved [2 pp. 74-92].

public class Adapter4Adaptee1 : IAdapter
{
    protected Adaptee1 target;
    
    public Adapter4Adaptee1(Adaptee1 target)
    {
          this.target = target;
    }

    public int this[int index]
    {
          get { return target[index]; }
          set { target[index] = value; }
    }

    public int X
    {
          get { return target.X; }
          set { target.X = value; }
    }

    public void Method(int x)
    {
          target.Method(x);
    }

    public event EventHandler Event1
    {
          add
    {       
        target.Event1 += value;
          }
          remove
          {     
        target.Event1 -= value;
          }
    }

    public void OnEvent1()
    {
          target.OnEvent1();
    }
}
Excerpt 3: A man made adapter

The class created in the previous code (Adapter4Adaptee1) delegates every member on Adaptee1 instances. The same can be done for adapting Adaptee2, and Adapter4Adaptee2 will look pretty much the same as Adapter4Adapter1. This example was a long code that doesn't do much, and I didn't even write both of the adapters. I think this task is a lot mechanical to be manmade.

Automatic Adapters

Creating Adapters can be a really boring and exhausting job, even more if they are all the same, just proxies. Fortunately, we have Reflection and Emit to do the work for us.

Maybe you don't like calling them Adapters, I don't either. They do both, they adapt different objects into a single compatible interface, and it's done by creating a proxy. So, they are supposed to be both. Since abstraction is needed on science, I'll call them adapters because that's what they are going to do for me and I won't care about their implementation.

Using Emit, I'll create a dynamic, single module, library assembly. Then, I will define an Adapter type for every pair of Adapter-Adaptee. Later, using a binding method, the client will be able to get wrapped into an Adapter adaptee. Lazy Adapter generation and some caching would be useful since Emit isn't a very fast feature.

The general interfaces for adapter builders are:

public interface IGenericAdapterBuilder
{
    AdapterInterfaceType BindAdapter<AdapterInterfaceType, 
    AdapteeType>(AdapteeType adaptee);
    AdapterInterfaceType BindAdapter<AdapterInterfaceType, 
    AdapteeType>(AdapteeType adaptee, Type adapterBaseType); 
    AdapterInterfaceType BindAdapter<AdapterInterfaceType>(object adaptee);
    bool CanBind<AdapterInterfaceType>(object adaptee);
}
  
public interface IAdapterBuilder
{
       object BindAdapter(Type adapterInterfaceType, Type adapteeType, object adaptee);
    object BindAdapter(Type adapterInterfaceType, Type adapteeType, object adaptee, 
    Type adapterBaseType);
    object BindAdapter(Type adapterInterfaceType, object adaptee);
    bool CanBind(Type adapterInterfaceType, object adaptee);
}
Excerpt 4: Adapter Builder Interfaces

Two choices: they are supposed to work the same. One does the cast for you; the other can be serialized and/or be used in .NET 1.1 or 1.0. Both have an availability check method; just in case, implicit checking is disabled for efficiency, a method CanBind will allow clients to prevent themselves from getting Exceptions. The current default implementation takes an assembly name, or creates a default random one, and fills this assembly with proxy-adapters.

An adapter builder will create a class implementing the adapter interface which relays every one of the adaptee's members. A single constructor should be created receiving an adaptee instance which should be stored in an instance field. It means an automatic proxy-adapter will look exactly like the class defined on Excerpt 3.

Now, let's take a look at how to use an Adapter Builder example using the adapter from Excerpt 1 and the adaptees from Excerpt 2.

...
AdapterBuilder builder = new AdapterBuilder();      
Adaptee1 adaptee1 = new Adaptee1();      

if (!builder.CanBind(typeof(IAdapter), adaptee1)) 
{       
    Console.WriteLine("Can't bind");
    return;
}      

IAdapter adapter1 = builder.BindAdapter<IAdapter, Adaptee1>(adaptee1);
adapter1.Method(1);

Console.WriteLine(adapter1.X);
adapter1.X = 2;
Console.WriteLine(adapter1.X);

adapter1[0] = 13;
Console.WriteLine(adapter1[0]);

adapter1.Event1 += new EventHandler(adapter1_Event1);
adapter1.Event1 += new EventHandler(adapter1_Event2);
adaptee1.OnEvent1();
IAdapter adapter2 = 
  builder.BindAdapter<IAdapter, Adaptee2>(new Adaptee2());

adapter2.Method(2);
Console.WriteLine(adapter2.X);
adapter2.X = 1;
Console.WriteLine(adapter2.X);
adapter2[10] = 31;
Console.WriteLine(adapter2[10]);
...
Excerpt 5: An Adapter builder in use

Succeeded!! My adapters behave just like their adaptees, and now we can use (through their adapters) instances from either adaptee class like if they were from the same hierarchy.

Starting the Magic

The following sections will show how to use proxies, which look like adapters, to change the way adaptees behave. To do so, I'll use the extensible declarative metadata (custom attributes) and several implementations for adapter builders.

Default Member Implementations for Interfaces

Sometimes we cannot use adapters, because adaptees don't expose the desired interface completely. What if we could provide the default implementation for absent members? Using custom attributes, we can provide an interface with the default member's implementation. Then, an adapter will relay on the implemented members or the default implementation if the adaptee doesn't implement all the members.

Let's take, for instance, this interface for an Adapter:

[DefaultImplementationType(ImplementationType = typeof(VectorImplementation))]
public interface IVector
{
    float X { get; set; }
    float Y { get; set; }
    float Length { get; }
    void Print();
    void Print(string format);
}
Excerpt 6: Bi-Dimensional Vector Adapter

Note the custom attribute DefaultImplementation initialized with the type VectorImplementation. We'll check out that type later. Right now, let's take a couple of adaptee candidates which can be used with this interface. I will create one called Vector and the other one will be System.Drawing.PointF.

public class Vector 
{
    float flt_X;
    public float X 
    {
          get { return flt_X; }
          set { flt_X = value; }
    }
    float flt_Y;
    public float Y
    {
          get { return flt_Y; }
          set { flt_Y = value;}
    }
    
    public float Length
    {
          get 
        { 
            return (float)Math.Sqrt(Math.Pow(flt_X, 2) 
                + Math.Pow(flt_Y, 2)); 
        }
    }
}
Excerpt 7: My own bi-dimensional Vector
public struct PointF
{
    public float X { get; set; }
    public float Y { get; set; }
    ...
}
Excerpt 8: .NET Framework built-in PointF struct

Neither PointF nor Vector defines any Print overload, and PointF doesn't have a definition for the property Length. Length depends only on the properties X and Y; Print will do the same this time. When creating the proxy, I'll rely on static methods from the type specified on the DefaultImplementation attribute for absent members. These static methods will look similar to the C# 3.5 Extension Methods [3].

public class VectorImplementation
{
    public static float get_Length(IVector _this)
    {
        return (float)Math.Sqrt(_this.X * _this.X 
            + _this.Y * _this.Y);
    }

    public static void Print(IVector _this)
    {
        Console.WriteLine("X={0}; Y={1}; Length={2}", 
            _this.X, _this.Y, _this.Length);
    }
    
    public static void Print(IVector _this, string format)
    {
        Console.WriteLine(format, _this.X, _this.Y, _this.Length);
    }
}
Excerpt 9: Default implementations holder

Let's take a look at how to use the DefaultImplementationAdapterBuilder with these two types (Vector and PointF).

AdapterBuilder builder = new DefaultImplementationAdapterBuilder("DynamicAdapters");
 
IVector vector = builder.BindAdapter<IVector, PointF>(new PointF());

vector.X = 3;
vector.Y = 4;     
Console.WriteLine(vector.Length);
vector.Print();

vector = builder.BindAdapter<IVector, Vector>(new Vector());

vector.X = 3;
vector.Y = 4;
Console.WriteLine(vector.Length);
vector.Print();
Excerpt 10: Using default implementations

DefaultImplementationAdapterBuilder's constructor parameter is just a name for the dynamic assembly that will contain the adapters' implementations; if left blank, an automatic name created from a GUID would be used. As you can see, after binding, we can use either Vector or PointF as if they were IVector. Be aware, Vector is a reference type and PointF is a value type, and both adapters are reference types.

Default Implementation Generalization

A first parameter with the same type as the adapter will be added to every method. All operations can be done through this parameter. Events default implementation didn't seem too much attractive and a little bit complicated, so it's been left out. Properties default implementation will be done by defining their accessors, like get_Length or the C# 3.5 auto-implemented properties [4] policy could be used (currently not implemented).

[DefaultImplementation(ImplementationType = typeof(DefaultImplementationIAdapter))]
public interface IAdapter
{
    Ret_Type Method(Param_Type1 p1, Param_Type2 p2, .., Param_Type1N pN) ;
    Property_Type Property     
    { 
        get ; set ; 
    }

    Indexer_Type this[Index_Type1 i1, Index_Type2 i2, .., Index_Typen iM]     
    { 
            get; set; 
    }
}
Excerpt 11: An adapter with default implementation attribute in pseudo C#

A complete default implementations set will be like this:

public static class DefaultImplementationIAdapter 
{
      public static Ret_Type Method(IAdapter _this, Param_Type1 p1, 
        Param_Type2 p2, .., Param_TypeN pN) 
    { 
        ... 
    }    

      public Property_Type get_Property(IAdapter _this) 
    { 
        ... 
    }

      public void set_Property(IAdapter _this, Property_Type _value) 
    { 
        ... 
    }    

      public Indexer_Type get_Item(IAdapter _this, Index_Type1 i1, 
        Index_Type2 i2, .., Index_TypeN iN) 
    { 
        ... 
    }

      public void set_Item(IAdapter _this, Index_Type1 i1, 
        Index_Type2 i2, .., Index_TypeN iN, Indexer_Type _value) 
    { 
        ... 
    }

}
Excerpt 12: Default implementations set in pseudo C#

Default Implementation Attributes

The basic attribute DefaultImplementationAttribute will define a type containing implementations (as described in Excerpt 9) for every member (not events) that could be missing. It has just one named parameter: ImplementationType, which must be set. I left it as a property for readability issues, but is actually mandatory.

An Attribute MemberImplementation can be used to override the source for the default implementation of a certain member and ForceImplementation to choose always the default implementation over adaptees' implementation, either from DefaultImplementationAttribute or MemberImplementationAttribute.

Priority for choosing implementation will work as follows:

  1. Forced Members, which will be found from 3 or 4
  2. Implemented members
  3. MemberImplementation
  4. DefaultImplementation

There are some redundant features, but they can be useful to avoid repeating code, and definitely cannot be used the wrong way, so no harm done.

Eiffel-like Separates

A Separate is an instance in which void members are executed on another thread than the caller [5]. Eiffel allows creating classes where every instance will be a separate object. My SeparateAdapterBuilder will allow creating only types in which every instance is a separate. They are adapters, so if you don't want a separate instance, you can still use the original instance, or a different adapter builder.

Implementing Separates using Delegates

A Separate adapter will define a delegate type for every void method, and then it will call this delegate asynchronously and return. This way, the "original" method will run on a different thread and the caller won't be blocked until the method finishes.

public class Separatee
{
    public void F(object x) { ... }
    public void G(object x) { ... }
    public void H() { ... }
}
Excerpt 13: An adaptee ready for Separation

This is how the Separate adapter looks:

public class SeparateAdapter
{
    Separatee _target;
    public Separate4Adaptee1(Separatee adaptee)
    {   
        _target = adaptee;
    }

    delegate void FDelg(object x);       
    public void F(object x)
    {  
        new FDelg(_target.F).BeginInvoke(x, null, null);
    }

    delegate void GDelg(object x);
    public void G(object x)
    {
        new GDelg(_target.G).BeginInvoke(x, null, null);
    }

    delegate void HDelg();
    public void H()
    {
        new HDelg(_target.H).BeginInvoke(null, null);
    }    
}
Excerpt 14: Separate Adapter

Some optimizations can be done for not repeating equivalent delegate types, but I wanted to end this someday. Another thing, actual separates are more complicated.

Locks

As I seem to remember, Eiffel puts a Monitor Lock on every actual parameter passed to a separate instance method. Using the implementation on Excerpt 14, it would be impossible. So actually, I'll build an additional method which will put the locks and call synchronously the adaptee's method, and this one will be the one executed asynchronously by the delegate. Quite a mess, isn't it?

public class LockedSeparateAdapter
{
    Separatee _target;
    
    public LockedSeparateAdapter(Separatee adaptee)
    {
        _target = adaptee;
    }

    delegate void FDelg(object x);
    void FImpl(object x)
    {
        lock (x)
        {
            _target.F(x);
        }
    }

    public void F(object x)
    {
        new FDelg(this.FImpl).BeginInvoke(x, null, null);
    }
}
Excerpt 15: Separate with locks

Voilà!! Now, my separates look pretty much the same as the Eiffel ones. I will allow you not to lock when you don't want to lock. To disable this option, use the attribute NoLocks. And, if you want all methods from a separate instance to be executed synchronously among them (like sharing a thread pool with just a thread), you could ask for a lock on this using LockThis on the methods you want to share the same pool.

public interface IAdapter
{
    [NoLocks]
    void F(object x);
    
    [LockThis]
    void G(object y);

    [LockThis]
    void H();
}
Excerpt 16: Use of LockThis Attribute

In the previous excerpt, G and H share the same thread pool, but F remains free to execute on yet another thread. Creating as many pools as wanted and sharing them freely is not currently implemented, but could be done easily by generating string instance fields on the adapter and locking them instead of this.

Injections

Allow to insert some code to be executed within an already implemented method. There're some really cool implementations of this. Mine just allows executing some code (e.g.: a method call) before and/or after a method. I don't think is one of the cool ones, but it's still useful.

Let's take the classic Log problem. Login entry and exit from every call on some type's methods.

public class UnderWatch
{ 
    public void Method1()
    {
        LogEnter();
        // Do something
        LogExit();
    }

    public void Method2(int x, int y)
    {
        LogEnter();
        // Do something
        LogExit();
    }

    private void LogEnter()
    {
        // Log code
    }

    private void LogExit()
    { 
        // Log code
    }
}
Excerpt 17: Log problem

Using this code, the problem would be solved for type UnderWatch. We could assume code inside Log* methods could be enabled/disabled easily. But, it's still uncomfortable having to write these lines on every single method. For making this job easier, we could inject code for logging on an adapter and use the adapter instead.

[Log]
public interface IWatchDog
{
    void Method1();
    void Method2(int x, int y);
}

public class UnderWatch1
{
    public void Method1()
    {
        Console.WriteLine("Adaptee1::Method1()");
    }

    public void Method2(int x, int y)
    {
        Console.WriteLine("Adaptee1::Method2(int {0}, int {1})", x, y);
    }
}

public class UnderWatch2
{
    public void Method1()
    {
        Console.WriteLine("Adaptee2::Method1()");
    }

    public void Method2(int x, int y)
    {
        Console.WriteLine("Adaptee2::Method2(int {0}, int {1})", x, y);
    }  
}
Excerpt 18: Adapter with logging

Attribute Log will create all the code we might need to inject to every member of IWatchDog. Then, an InjectionsAdapterBuilder will create an injected WatchDogAdapter.

static void Main(string[] args)
{
    IGenericAdapterBuilder builder = new InjectionsAdapterBuilder();
 
    IAdapter adapter1 = builder.BindAdapter<IAdapter>(new Adaptee1());
    adapter1.Method1();
    adapter1.Method2(0, 1);
 
    IAdapter adapter2 = builder.BindAdapter<IAdapter>(new Adaptee2());
    adapter2.Method2(0, 1);
}
Excerpt 19: Using the WatchDog

This code will dump into the standard output plenty of information about the method on entering it, then it will execute the method, and again a lot of information before it leaves. Parameters and return aere not used for this Log implementation, but could be added easily.

Injections under the Microscope

Injections will be specified using Attributes. So, any attribute implementing Preinjection or Postinjection would be an injections provider. That means they could provide code for injecting before method execution or after. If the injected code doesn't work well, the application will fail for sure, so take a good look at IL books before trying to write your own. This adapter builder will try to find injection attributes on members and their containing types. So, attributes applied to types apply to every member. Again, other implementations wouldn't be difficult.

public class LogAttribute : Attribute, 
       IPreInjectionAttribute, IPostInjectionAttribute
{
    ...
}

public interface IPreInjectionAttribute
{
    IInjection Injection { get; }
}

public interface IPostInjectionAttribute
{
    IInjection Injection { get; }
}
Excerpt 20: Attributes and Interfaces for Injections

Pre and post injection attribute interfaces are the same because I wasn't able to find a valid combination of explicit implementations so I could provide two implementations for the same method using only one base interface. Another way an attribute wouldn't be able to implement both, pre and post, with different results: any ideas?

Finally, we need to know what an injection is:

public interface IInjection
{
    MethodInfo InjectionSource { get; set; }
    Type ResolutionContext { get; set; }
    IList<Assembly> References { get; }

    void Inject(ILGenerator generator, MethodBuilder method, TypeBuilder container);
}
Excerpt 21: Injection Interface

Why so many fields? InjectionSource is the method on the adapter interface, ResolutionType is the adapter interface type (which I could get from InjectionSource, redundant but useful), and References might allow me to find some foreign types or …I don't know. Usually, I try to think about everything I might need, and usually, I fail. The only method: Inject does the injection, just like that, we give it the method we are creating for our adapter and it injects the code. See how it could break your application.

Injections: Conclusion

Using just some attributes and the injections adapter, we could pre inject and post inject some code. This by itself isn't that interesting. But, there are some very great and very old ideas that can be done using injections…

Design by Contract

Bertrand Meyer's Metaphor on Design by Contracts [5] allows creating specifications on components (classes for Eiffel) and also verifying their fulfillment in runtime. Some years ago, I worked in an implementation of DxC [6]. At that time, me and my buddies built a small C# parser for compiling assertions into imperative verification code. We also provided a mechanism to insert this verification code as injections. Now, I'm going to show how to inject the same code using a different approach. Let's take a look at the following Eiffel implementation for a Stack.

class Stack
    invariant
        Count >= 0
    feature
        Push(X : Object) is
        require
            X <> void
            not Full
        do
            ...
        ensure
            Count = old Count + 1
            Top = X
            ! Empty
        end
        Pop is
        require
            not Empty
        do
            ...
        ensure
            Count = old Count – 1
        end
        ...
end
Excerpt 22: Stack in Eiffel

Previous excerpts might not be accurate, it's been some really long time since I saw Eiffel for the last time, but the idea is there. Invariant defines the consistent state of an object; it must be fulfilled every time a method is called from a client and before it returns to the client. Precondition, under clause require, must be fulfilled every time a method is called; Postcondition, under clause ensure, must be fulfilled before the method returns. Operator Old refers to the value affected expression before executing the method, it can be applied to any expression.

Once again, using attributes, I could create an adapter like this:

[Invariant("Count >= 0"]
public interface IStack
{
    ...
    [Require("! Full")] 
    [Ensure("Count == old Count + 1")]
    [Ensure("Top == x")]
    [Ensure("! Empty")]
    void Push(object x) ;
 
    [Require("! Empty")]
    [Ensure("Count == Count.Old - 1")]
    void Pop() 
}
Excerpt 23: Stack with assertions

An AssertionAdapter should then generate code for checking assertions. I will use my old compiler. Since I started working in that project when I was still learning about compilers and hadn't yet heard of compiler generators, I created my own. It is old and is not very good, so please be gentle. Still, it's useful in this case, because it works. Once again, it's more than five years old.

Quatifiers

It's been commented that Eiffel assertions' lack for expressivity in some cases [9]. The fact is assertions are propositions when predicates are more expressive logical expressions. When I built my very old C# parser, I included that feature using quantifiers (both Universal and Existential) over IEnumerable expressions. Then again, no really cool examples come to my mind, so here goes nothing.

[Invariant("forall(object e in this: Contains(e))")]
public interface ISet : IEnumerable
{
    bool Contains(object x);
    [Require("forall(object element in this: element != x)")]
    [Ensure("exists(object element in this: element == x)")]
    void Add(object x);
}
Excerpt 24: Using quantifiers

Tomorrow

I think with these ideas and some typing, some very useful tools could be created. For instance, using Injections, AOP could be implemented, without the need of precompilers or having to put it as part of a any language. I'll try to do some more.

References

  • [1] Ledesma, Erich. Dynamic Encapsulation: On and off line hierarchy modification. The Code Project. [Online] February 19, 2008. http://www.codeproject.com/KB/cs/dynamic_encapsulation.aspx.
  • [2] Bishop, Judith. C# 3.0 Design Patterns. [ed.] John Osborn. 1st Edition. s.l.: O'Reilly, 2007. pp. 22-35;74-92. ISBN 10: 0-596-52773-X; ISBN 13: 978-0-596-52773-0.
  • [3] Extension Methods (C# Programming Guide). MSDN: Visual C# Developer Center. [Online] Microsoft, November 2007. http://msdn.microsoft.com/en-us/library/bb383977.aspx.
  • [4] Auto-Implemented Properties (C# Programming Guide). MSDN: Visual C# Developer Center. [Online] Microsoft, November 2007. http://msdn.microsoft.com/en-us/library/bb384054.aspx.
  • [5] Meyer, Bertrand. Object Oriented Software Construction. 2nd Edition. s.l.: Prentice Hal, 2000. ISBN: 0136291554.
  • [6] Including Assertions in .NET Assemblies. Katrib, Miguel, Ledesma, Erich and Paneque, Leonardo. 11, September 2003, .NET Developers Journal.
  • [7] Lidin, Serge. Inside Microsoft .NET IL Assembler. s.l.: Microsoft Press, 2002. ISBN: 0-7356-1547-0.
  • [8] Gamma, Erich, et al. Design Patterns: Element of Reusable Object-Oriented Software. s.l.: Addison-Wesley, 1995. ISBN: 0201633612.
  • [9] Katrib M, Coira J. Improving Eiffel Assertions Using Quantified Iterators. Journal of Object Oriented Programming. Nov, 1997.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Erich Ledesma
Software Developer SunHotels
Spain Spain
I Received a Bachelor's Degree in Computer Science at the Mathematics and Computer Science Faculty, University of Havana, Cuba.
 
I mainly work in web applications using C# and some Javascript. Some very few times do some Java.

Comments and Discussions

 
GeneralCorrupted Links. Pinmemberring_018-Feb-09 18:16 
GeneralRe: Corrupted Links. PinmemberErich Ledesma18-Feb-09 22:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 21 Apr 2009
Article Copyright 2009 by Erich Ledesma
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid