Click here to Skip to main content
15,881,812 members
Articles / Programming Languages / C#
Article

STL Dotnet

Rate me:
Please Sign up or sign in to vote.
4.25/5 (8 votes)
19 Oct 20052 min read 38.2K   261   28   2
STL for C#.

Introduction

Everybody knows the Standard Template Library provided with most C++ compilers. Here is a proposition of STL for C# generics and .NET class design.

The .NET framework already provides many containers, but I found it could be useful to find some patterns we used to practice earlier, in particular, algorithms and binders.

Using the code

The source code provided in the downloads section is a complete Visual Studio 2005 project with the solution file. The assembly can be used in any project without restriction.

Note: It is assumed that the reader is familiar with all generic classes and interfaces of the .NET framework 2.0. This is the basis for using and understanding this library.

Algorithm

  • manipulation of collections is done through IEnumerable<T>.
  • results as IEnumerable<T> does not use memory, and the real calculus of each item of the result is done at each iteration on it (you can see it as an evaluation tree).
  • delegates allow to provide a "behind object" for context persistence on each calculus, and replace with advantages C++ functors or function (member or not) pointer templates armada.
  • for finite results, avoid infinite sets. That's a best practice, a good design, and that's why such algorithms as Iota require a count parameter.
C#
public static class Algorithms : object
{
  public static IEnumerable<T> 
         PartialSum<T>(IEnumerable<T> values, 
         BinaryFunction<T, T, T> plus) ;
  public static IEnumerable<T> 
         AdjacentDifference<T>(IEnumerable<T> values, 
         BinaryFunction<T, T, T> minus) ;
  public static T Accumulate<T>(IEnumerable<T> v, 
         BinaryFunction<T, T, T> plus, T zero) ;
  public static T InnerProduct<Arg1, Arg2, 
         T>(IEnumerable<Arg1> v0, IEnumerable<Arg2> v1, 
         BinaryFunction<Arg1, Arg2, T> multiplies, 
         BinaryFunction<T, T, T> plus, T zero) ;
  public static IEnumerable<T> Transform<Arg, 
         T>(IEnumerable<Arg> v, 
         UnaryFunction<Arg, T> f) ;
  public static IEnumerable<T> Transform<Arg1, Arg2, 
         T>(IEnumerable<Arg1> v0, IEnumerable<Arg2> v1, 
         BinaryFunction<Arg1, Arg2, T> f) ;
  public static IEnumerable<T> 
         Transform<T>(IEnumerable<T> v0, 
         IEnumerable<T> v1, BinaryFunction<T, T, T> f) ;
  public static IEnumerable<T> 
         Transform<T>(IEnumerable<T> v, 
         UnaryFunction<T, T> f) ;
  public static IEnumerable<T> 
         RemoveIf<T>(IEnumerable<T> v, 
         Predicate<T> predicate) ;
  public static IEnumerable<T> 
         FindIf<T>(IEnumerable<T> v, 
         Predicate<T> predicate) ;
  public static IEnumerable<T> 
         AdjacentFind<T>(IEnumerable<T> v, 
         Relation<T> relation) ;
  public static IEnumerable<T> 
         ReplaceIf<T>(IEnumerable<T> v, 
         Predicate<T> predicate, T value) ;
  public static IEnumerable<T> 
         Unique<T>(IEnumerable<T> v) ;
  public static bool Exist<T>(IEnumerable<T> v, 
         Predicate<T> predicate) ;
  public static bool ForAll<T>(IEnumerable<T> v, 
         Predicate<T> predicate) ;
  public static int Count<T>(IEnumerable<T> v) ;
  public static int CountIf<T>(IEnumerable<T> v, 
         Predicate<T> predicate) ;
  public static IEnumerable<T> Generate<T>(int count, 
         Generator<T> f) ;
  public static IEnumerable<T> Iota<T>(int count, 
         BinaryFunction<T, T, T> plus, T zero,T step) ;
  public static IEnumerable<T> Iota<T>(int count, 
         UnaryFunction<T, T> f, T zero) ;
}

Sort and merge operations are not here for the moment. Sets operations were not interesting for me because lots of work on Sets could already be found on CodeProject.

Functional

Some standard prototypes are defined through delegates. It allows developers to use objects without having to inherit some interfaces, and as we'll see later, it is not so inefficient as we think.

C#
public delegate T Generator<T>();
public delegate T UnaryFunction<Arg, T>(Arg x);
public delegate T BinaryFunction<Arg1, Arg2, T>(Arg1 x, Arg2 y);
// public delegate bool Predicate<T>(T obj);
public delegate bool Relation<T>(T x,T y);

Generics in .NET changes some practices we used to have with C++ templates. They provide categories for atomic operations on numbers. They are not necessary, because, type like Decimal has their own functions (add, subtract...). I noticed Double didn't provide these functions, and I don't understood why...

C#
public static class Plus : object ;
public static class Minus : object ;
public static class Multiplies : object ;
public static class Divides : object ;
public static class Modulus : object ;
public static class Negate : object ;

Combining some existing and "unmodifiable" functionalities without writing code is the main interest of binders. And it allows dynamic approaches. They are defined as static functions in the Binders class.

  • Bind1st x,y->f(x,y) |- y->f(a,y)
  • Bind2nd x,y->f(x,y) |- x->f(x,a)
  • Compose x->f(x) , x->g(x) |- x->f(g(x))
  • Compose2nd x,y->f(x,y) , x->h(x) |- x,y->f(x,h(y))
  • Compose1st x,y->f(x,y) , x->h(x) |- x,y->f(h(x),y)
  • Symetric x,y->f(x,y) |- x,y->f(y,x)

And now, a short example to begin with:

C#
// some constants
IEnumerable<double> v0 = 
  new double[] { 1.1, 2.234, 3.1415 , 5.879 , 1.47896 };

// U0 = 1 and Un+1 = Un + 0.1
IEnumerable<double> v1 = 
  Algorithms.Iota<double>(5, Plus.Double, 1, 0.1);

// q = v0 / v1
IEnumerable<double> q = 
  Algorithms.Transform(v0, v1, Divides.Double);

int digits = 2;
UnaryFunction<double, double> round = 
  Binders.Bind2nd<double, int, 
  double>(Math.Round, digits);

// prepare all to be rounded at 2 digits
q = Algorithms.Transform<double>(q, round);

StringBuilder sb = new StringBuilder(1024);

// do the effective calculus
foreach (double x in q)
    sb.AppendLine(x.ToString());

There is no need to store results. It's interesting if you don't have to calculate all the values. But if needed, create a new List. Having an in place transformation effect on all classes seem to be meaningless, but it could be wished for any Array<T>. To me, a writer pattern is the good solution.

Points of Interest

  • This sample demonstrates generics are powerful and efficient.
  • The code has a high abstraction level.

History

  • First version - 10/19/2005.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Sweden Sweden
Frédéric DIDIER is a 4 years experienced .Net developper. He has written a sawmill complete critical-time optimization engine in C# (Log-bucking, breakdown, small log processing ,
edger ,trimmer , harvest-parc simulation, short log sorting), improving efficiency and quality of professional existing applications written with MFC, demonstrating the superirity of .Net Framework in designing classes and applications.

Comments and Discussions

 
GeneralMicrosoft's .Net enumerator limitations Pin
Edward Diener30-Mar-07 3:19
Edward Diener30-Mar-07 3:19 
GeneralRe: Microsoft's .Net enumerator limitations Pin
Frédéric DIDIER6-Apr-07 10:36
Frédéric DIDIER6-Apr-07 10:36 

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

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