What is a Func<T> or an Action<T>?
How is it useful?
Inside BPMs, UBAQ BPMs, or Epicor Functions (BPMs who drink wine with their pinky out), you are inside a single method call. So you cannot define your own methods inside like you could in a traditional class based system.
That kind of sucks.
There are a few workarounds for organizing your code, and the items we will discuss today will be helpful to work around such limitations.
Ok so what are they?
Technically, they are called delegates.
So what is a delegate?
Well Microsoft says:
A delegate is a type that represents references to methods with a particular parameter list and return type. When you instantiate a delegate, you can associate its instance with any method with a compatible signature and return type. You can invoke (or call) the method through the delegate instance.
I will not go into it any further than that, but Func<T> & Action<T> are pre-defined delegates, that are also Generic. I will not cover generic methods and types in this discussion other than to say that they can be defined to work on the types that we want.
Ok, well to me that still sounds like gobledy-gook. How would I use one?
Ok, well letās imagine we are inside a BPM, in a custom code blockā¦
//Ignore my lack of null checking please...
//And I have this code here that gets the first letter of a string
string firstLetter = someString.Substring(0, 1);
//And I keep using it and using it and decide I want something cleaner
foreach(string s in someListOfStrings)
{
DoSomethingWithThis = s.Substring(0, 1);
}
var myList = new List<string>();
if(someOtherString5000.Substring(0, 1) == "f")
{
myList.Add(someOtherString);
}
And you wanted to clean it up a bit.
You could do the following:
Func<string, string> GetFirstLetter = (s) =>
{
if(String.IsNullOrEmpty(s)) return null;
return s.Substring(0, 1);
};
string firstLetter = GetFirstLetter(someString);
foreach(string s in someListOfStrings)
{
DoSomethingWithThis = GetFirstLetter(s);
}
var myList = new List<string>();
if(GetFirstLetter(someOtherString5000) == "f")
{
myList.Add(someOtherString);
}
Ok, I see how that could be useful. Anything else I need to know?
Yes, a few.
First, Func<T> & Action<T> must be defined before you use them, so they need to be somewhere above any code and in scope. Think of it like a variable, because it really is a variable. It just holds a method, instead of an object or value.
OK, well how do I make one, and what is the difference between the two?
Ok, second question first.
The difference between the two is you will use Func<T> when you need to return values from it, and Action<T> when you do not. You can also use Func<T> and not use the return from it, but you must have a return.
Ok, well what is this <T> bit?
Iām getting to it. (Itās the generic bit, that Iām not really discussing lol)
The way you define a Func<T>:
In short, like this:
Func<[inputType(s)], [returnType]> [MethodName] = ( [inputVariableName] ) =>
{
//Do work here
return [returnType];
};
//Like...
Func<string, string> GetFirstLetter = (s) =>
{
if(String.IsNullOrEmpty(s)) return null;
return s.Substring(0, 1);
};
The way you define an Action<T>:
In short, like this:
Action<[inputType(s)]> [MethodName] = ( [inputVariableName] ) =>
{
//Do work here
//No return
};
//Like...
Action<string> ShowAMessageOrSomethingCool = (s) =>
{
if(String.IsNullOrEmpty(s)) return;
InfoMessage.Publish(s);
};
Hey, I caught that, you have ā[inputType(s)]ā up there. Huh?
Well yes, you can pass more than one argument if needed, like so:
Func<string, bool, string> GetFirstLetter = (s, ifNotA) =>
{
if(String.IsNullOrEmpty(s)) return null;
string temp = s.Substring(0, 1);
if(ifNotA == true)
{
if(temp == "A") return null;
}
return temp;
};
And thatās pretty much it.
But wait! What in the world is that ( ) => { }; crazy stuff?
Oh that? That is what is called a Lambda, and that my friend is a topic for another day. For now, just follow directionsā¦