Another attempt at Functions as objects.
One of the challenges in the Method class is that C# doesn’t let me apply generics as signatures all over the place. I would have liked to make the delegate type a type parameter, and somehow specify that Method.Invoke matched the signature of the delegate, etc. But it just can’t be done.
So Cyrus suggested I limited to just functions from A to B:
delegate B Function<A, B> (A a);
I gave it a try but ran in to snag with methods that have no return type (‘void’) or no parameters (‘()’). The suggestion was to consider 2 more signatures:
delegate A Creator();
delegate void Action<A> (A a);
So, I created 3 classes, of for each type of signature.
Another thing I got stuck on was naming. I could have done:
delegate B Function<A, B> (A a);
class FunctionClass<A, B>
{
// ...
}
Yuck on the name. Also got a lot of repetition of the type parameters throughout the class. So, I tried instead to move the delegate type into the class:
class Function<A, B>
{
public delegate B Delegate(A a);
// ...
}
It seemed to be a bit cleaner.
I have a common base class for the 3 function types, but:
1. I don’t know what to call it, so I gave it the dumbest name I could think of.
2. I couldn’t push as much into the base as I would like. There’s still a lot of duplication here, but I’m limited by the rules of the language.
Enough blabber, here’s the code:
class StartEndEvent : IDisposable
{
public event EventHandler OnStart;
public event EventHandler OnEnd;
object _sender;
public IDisposable Open(object sender)
{
this._sender = sender;
this.OnStart(_sender, null);
return (IDisposable)this;
}
void IDisposable.Dispose() { this.OnEnd(_sender, null); }
}
abstract class FunctionAbstractBase
{
// must be signed because 'Interlocked.Increment (ref uint)' doesn't exist.
int _currentExecutingThreadCount = 0;
public bool IsExecuting { get { return _currentExecutingThreadCount > 0; } }
public StartEndEvent StartEndEvent = new StartEndEvent();
protected FunctionAbstractBase()
{
StartEndEvent.OnStart += delegate { System.Threading.Interlocked.Increment(ref this._currentExecutingThreadCount); };
StartEndEvent.OnEnd += delegate { System.Threading.Interlocked.Decrement(ref this._currentExecutingThreadCount); };
}
}
class Function<A, B> : FunctionAbstractBase
{
public delegate B Delegate(A a);
public readonly Delegate Value;
public Function(Delegate function) : base() { this.Value = function; }
public B Invoke(A value)
{
using (this.StartEndEvent.Open(this))
{
return this.Value(value);
}
}
public static implicit operator Delegate(Function<A, B> m) { return m.Invoke; }
public static implicit operator Function<A, B>(Delegate d) { return new Function<A, B>(d); }
}
class Action<A> : FunctionAbstractBase
{
public delegate void Delegate(A a);
public readonly Delegate Value;
public Action(Delegate function) : base() { this.Value = function; }
public void Invoke(A value)
{
using (this.StartEndEvent.Open(this))
{
this.Value(value);
}
}
public static implicit operator Delegate(Action<A> m) { return m.Invoke; }
public static implicit operator Action<A>(Delegate d) { return new Action<A>(d); }
}
class Creator<A> : FunctionAbstractBase
{
public delegate A Delegate();
public readonly Delegate Value;
public Creator(Delegate function) : base() { this.Value = function; }
public A Invoke()
{
using (this.StartEndEvent.Open(this))
{
return this.Value();
}
}
public static implicit operator Delegate(Creator<A> m) { return m.Invoke; }
public static implicit operator Creator<A>(Delegate d) { return new Creator<A>(d); }
}
Which of course needs a sample of usage:
// example usage
class MyClass
{
// this is a verbose way of saying
// void Method1(string)
// {
// //...
// }
//
public readonly Action<string> Method1 = new Action<string>(delegate(string s)
{
System.Console.WriteLine("Method1 was called - [{0}]", s);
});
}
MyClass c = new MyClass();
// the normal way to invoke
c.Method1.Invoke("Method1.Invoke");
// you can also pass c.Method1 around as a delegate
Action<string>.Delegate f = c.Method1;
f("via a delegate");