Blocks In Csharp


C# 2.0 (a.k.a Whidbey) now supports anonymous methods (blocks) and Icon-style iterators.

Anonymous methods allow you to return a closure from another method (a la Smalltalk blocks, Ruby blocks, Lisp lambda's, etc.):

 delegate T Function<T>(); // boilerplate static decl of signature

Function<int> counterFrom( int n ) { return delegate { return n++; }; // parens aren't needed unless there's an argument passed to the anonymous method. }

Function<int> counter = counterFrom( 3 ); Console.WriteLine(counter()); >>> 3 Console.WriteLine(counter()); >>> 4 Console.WriteLine(counter()); >>> 5

Iterators allow you to "push" a sequence of values into a consumer that iterates using the foreach language construct (similar to Ruby's each method):

 IEnumerable<int> counterFrom( int n ) {
   while (true)
     yield return n++;
 }

foreach (int n in counterFrom(3)) { if (n > 5) break; Console.WriteLine(n) } >>> 3 >>> 4 >>> 5


This is a CSharp version of RobertDiFalcos excellent page BlocksInJava, having loved this page for a while and being envious of Smalltalk for some time, I finally got around to translating it to CSharp, since I can't have anonymous delegates until 2005. See BlocksInJava for an explanation of how to use this. CSharp doesn't have anonymous inners, so the Block class isn't used, but this can be used for expression composition rather than specialization. RamonLeon

    public interface Predicate {
        bool Is();
    }
    public interface UnaryPredicate {
        bool Is(object anArg);
    }
    public interface BinaryPredicate {
        bool Is(object firstArg, object secondArg);
    }
    public interface Function {
        object Eval();
    }
    public interface UnaryFunction {
        object Eval(object anArg);
    }
    public interface BinaryFunction {
        object Eval(object firstArg, object secondArg);
    }
    public interface Procedure {
        void Run();
    }
    public interface UnaryProcedure {
        void Run(object anArg);
    }
    public interface BinaryProcedure {
        void Run(object firstArg, object secondArg);
    }
    public class And:BinaryPredicate {
        public bool Is(object firstArg, object secondArg) {
            return (bool)firstArg&&(bool)secondArg;
        }
    }
    public class Or:BinaryPredicate {
        public bool Is(object firstArg, object secondArg) {
            return (bool)firstArg||(bool)secondArg;
        }
    }
    public class Equals:BinaryPredicate {
        public bool Is(object firstArg, object secondArg) {
            return firstArg.Equals(secondArg);
        }
    }
    public class Same:BinaryPredicate {
        public bool Is(object firstArg, object secondArg) {
            return (bool)firstArg==(bool)secondArg;
        }
    }
    public class Less:BinaryPredicate {
        public bool Is(object firstArg, object secondArg) {
            return ((IComparable)firstArg).CompareTo(secondArg)<0;
        }
    }
    public class Greater:BinaryPredicate {
        public bool Is(object firstArg, object secondArg) {
            return ((IComparable)firstArg).CompareTo(secondArg)>0;
        }
    }
    public class Not:UnaryPredicate {
        public bool Is(object firstArg) {
            return !(bool)firstArg;
        }
    }
    public class BinaryCompose:UnaryPredicate {
        BinaryPredicate anOperator;
        UnaryPredicate  firstArg;
        UnaryPredicate  secondArg;

public BinaryCompose(BinaryPredicate anOperator, UnaryPredicate firstArg, UnaryPredicate secondArg) { this.anOperator = anOperator; this.firstArg = firstArg; this.secondArg = secondArg; } public bool Is(object anArg) { return anOperator.Is(firstArg.Is(anArg),secondArg.Is(anArg)); } public static BinaryCompose And(UnaryPredicate firstArg, UnaryPredicate secondArg) { return new BinaryCompose(new And(), firstArg, secondArg); } public static BinaryCompose Or(UnaryPredicate firstArg, UnaryPredicate secondArg) { return new BinaryCompose(new Or(), firstArg, secondArg); } } public class UnaryCompose:UnaryPredicate { UnaryPredicate firstPredicate; UnaryPredicate secondPredicate;

public UnaryCompose(UnaryPredicate firstPredicate, UnaryPredicate secondPredicate) { this.firstPredicate = firstPredicate; this.secondPredicate = secondPredicate; } public bool Is(object anArg) { return firstPredicate.Is(secondPredicate.Is(anArg)); } public static UnaryCompose Not(UnaryPredicate aPredicate) { return new UnaryCompose(new Not(),aPredicate); } } public abstract class AbstractBinder:UnaryPredicate { BinaryPredicate thePredicate; object theBoundArg;

public bool IsBound() { return theBoundArg != null; } public abstract bool Is(object anArg); public void Bind(object anArg) { theBoundArg = anArg; } public void Adapt(BinaryPredicate aPredicate) { thePredicate = aPredicate; } protected bool DoIsAsFirst(object anArg) { return thePredicate.Is(anArg, theBoundArg); } protected bool DoIsAsSecond(object anArg) { return thePredicate.Is(theBoundArg, anArg); } } public class BinderFirst:AbstractBinder { public BinderFirst(BinaryPredicate aPredicate):this(aPredicate,null) {} public BinderFirst(BinaryPredicate aPredicate, object anArg) { Adapt(aPredicate); Bind(anArg); } public override bool Is(object anArg) { return DoIsAsSecond(anArg); } } public class BinderSecond:AbstractBinder { public BinderSecond(BinaryPredicate aPredicate):this(aPredicate, null) {} public BinderSecond(BinaryPredicate aPredicate, object anArg) { Adapt(aPredicate); Bind(anArg); } public override bool Is(object anArg) { return DoIsAsFirst(anArg); } }

/// <summary> /// Higher Order function's with the new microsoft style naming convention that /// will be used in C sharp 2.0. These are done STL style so that all algorithms /// work on enumerators... these methods should work with any container. /// </summary> public class Iterate { public static object GetFirst(IEnumerable aList) { foreach(object anObject in aList) return anObject; return null; } public static object GetSecond(IEnumerable aList) { return GetFirst(GetRest(aList)); } public static object GetThird(IEnumerable aList) { return GetFirst(GetRest(GetRest(aList))); } public static object GetFourth(IEnumerable aList) { return GetFirst(GetRest(GetRest(GetRest(aList)))); } public static object GetFifth(IEnumerable aList) { return GetFirst(GetRest(GetRest(GetRest(GetRest(aList))))); } public static IList GetRest(IEnumerable aList) { IList result = Merge(aList,new ArrayList()); result.Remove(GetFirst(aList)); return result; } public static IList Merge(IEnumerable aList, IList anotherList) { foreach(object anObject in aList) anotherList.Add(anObject); return anotherList; } public static IList FindAll(IEnumerable aList, UnaryPredicate match) { ArrayList result = new ArrayList(); foreach(object anObject in aList) if(match.Is(anObject)) result.Add(anObject); return result; } public static IList FindAllNot(IEnumerable aList, UnaryPredicate match) { ArrayList result = new ArrayList(); foreach(object anObject in aList) if(!match.Is(anObject)) result.Add(anObject); return result; } public static object Find(IEnumerable aList, UnaryPredicate match) { foreach(object anObject in aList) if(match.Is(anObject)) return anObject; return null; } public static object FindLast(IEnumerable aList, UnaryPredicate match) { foreach(object anObject in Reverse(aList)) if(match.Is(anObject)) return anObject; return null; } public static object Inject(IEnumerable aList, object aValue, BinaryFunction aBlock) { foreach(object anObject in aList) aValue = aBlock.Eval(aValue,anObject); return aValue; } public static bool Exists(IEnumerable aList, UnaryPredicate match) { foreach(object anObject in aList) if(match.Is(anObject)) return true; return false; } public static bool TrueForAll(IEnumerable aList, UnaryPredicate match) { foreach(object anObject in aList) if(!match.Is(anObject)) return false; return true; } public static void RemoveAll(IList aList, UnaryPredicate match) { IList toRemove = FindAll(aList, match); foreach(object anObject in toRemove) aList.Remove(anObject); } public static void ForEach(IEnumerable aList, UnaryProcedure toRun) { foreach(object anObject in aList) toRun.Run(anObject); } public static void ForEachChild(Control aControl, UnaryProcedure aProc) { aProc.Run(aControl); foreach(Control aChild in aControl.Controls) ForEachChild(aChild,aProc); } public static IList ConvertAll(IEnumerable aList, UnaryFunction toEvalOn) { ArrayList result = new ArrayList(); foreach(object anObject in aList) { object processed = toEvalOn.Eval(anObject); if(processed!=null)result.Add(processed); } return result; } public static int Matches(IEnumerable aList, UnaryPredicate match) { int result=0; foreach(object anObject in aList) if(match.Is(anObject)) result++; return result; } public static IList Reverse(IEnumerable aList) { Stack aStack = new Stack(); foreach(object each in aList)aStack.Push(each); ArrayList result = new ArrayList(); while(aStack.Count>0)result.Add(aStack.Pop()); return result; } }


Why oh why oh why did they use the keyword "delegate" instead of "do"? Didn't the language designers think about how code is read? It's the same as saying "if you're hungry delegate eat a banana" instead of "if you're hungry do eat a banana". In code terms, this would read so much more pleasantly:

 button.Click += do { window.Close(); }

Or

 employees.Each( do(Employee e) { e.Salary += bonus; });

Fixed to some extent by the lambda operator added in 3.0. => is read as "goes to", according to the docs.

employees.Each( (e) => e.Salary += bonus );


See also CoroutinesInDotNet for a link to a implementation of coroutines using fibers.


CategoryCeeSharp CategoryClosure


EditText of this page (last edited January 17, 2013) or FindPage with title or text search