If we had a funcional language (eg Lisp or F#), we could easily manage a collection or list.
With the .NET Framework 2.0, we can work equally functionally with Generics (Exploring). using this feature of the CLR we can develop, process and manage our data more easily, especially with anonymous delegates.
An example that I wish to use, and is also the most frequent that I happened to develop, is the use of these anonymous delegates to the search function Find() of a generic list (List<T>).
Instead of writing code with foreach
loops every time I need to find an element in a set, I use a predicate that does the job in my place, so I can keep the code cleaner and I can focus on “things” more interesting.
The easiest way to use the Find()
method with a Predicate is included in this C# code:
public class person
{
public int Anni { get; set; }
public string Nome { get; set; }
public person(int anni, string nome)
{
this.Anni = anni;
this.Nome = nome;
}
}
List<person> agenda = new List<person>();
string personToBeSearched = "mario";
person mario = agenda.Find(delegate(person p) { return p.Nome == personToBeSearched; });
Using the Lambda Expression(Exploring) instead, introduced in the .NET Framework 3.5, the code is reduced even more:
List<person> agenda = new List<person>();
string personToBeSearched = "mario";
person mario = agenda.Find(p => p.Nome == personToBeSearched;);
You can also write an explicit predicate, if you do not like seeing too terse code:
private static bool findTeen(person p)
{
if (p.Anni < 18)
return true;
return false;
}
List<person> BlockBuster_GameOnly = agenda.findAll(findTeen);
We can refine the code even more to take advantage of this methodology not only for the Find()
method, but also to other cases of use. Indeed, we can expand our class that represents the data by overriding the Equals()
method, as in that other C# example:
public class person
{
public int Anni { get; set; }
public string Nome { get; set; }
public person(int anni, string nome)
{
Anni = anni;
Nome = nome;
}
public override bool Equals(object obj)
{
if (obj == null) return false;
person p2 = obj as person;
if ((object)p2 == null) return false;
return (this.Anni == p2.Anni) && (this.Nome == p2.Nome);
}
public override bool Equals(person p2)
{
if ((object)p2 == null) return false;
return (this.Anni == p2.Anni) && (this.Nome == p2.Nome);
}
public override int GetHashCode()
{
return this.Anni ^ this.Nome.Length;
}
}
In this way, we cannot just do a search based on a single property, but also look for “entities” within a specific list:
person Glauco = new person(36, "Glauco");
if (agenda.Find(p => p.Equals(Glauco)) == null)
agenda.Add(Glauco);
As you can see from the code, it can be very intuitive and easy to write lambda expressions in simple cases like these, but what happens if we have lists that contain very complex objects? For example, an object that identifies a testCrash
of a passenger car, or an object that represents a measurement of a numerical control system for an assembly line, rather than for the detection data of a station’s gas plant. These objects may be very complex and the code becomes almost unreadable. Better to opt for the explicit predicates version. But even in this case, we write a predicate for each different search criteria or for any sort order that we may serve.
We solve by creating a “class predicate” that allows parameters and displays the search
method. In this way, we can drastically reduce the predicates that we have to write (although it will be virtually impossible to reduce us to one). We also see a simple example for this case (not to complicate this article) always written in C#:
private class EnvironmentMonitoringResult
{
}
private enum CostructormanagerType
{
gasMetano = 0,
gasButano,
}
private class myPredicate
{
private CostructormanagerType impianto;
private bool dummy;
private string Parametro1;
public myPredicate(CostructormanagerType type)
{
this.impianto = type;
}
public myPredicate(CostructormanagerType type, bool isDummy)
{
this.impianto = type;
this.dummy = isDummy;
}
public bool findTipo1(EnvironmentMonitoringResult risultatoMisurazione)
{
bool findOK = true;
switch (this.impianto)
{
case CostructormanagerType.gasMetano:
findOK &= risultatoMisurazione.isNotturno = this.dummy;
findOK &= risultatoMisurazione.Parametro1 = this.Parametro1;
break;
case CostructormanagerType.gasButano:
break;
default:
break;
}
return findOK;
}
public bool findTipo2(EnvironmentMonitoringResult risultatoMisurazione)
{ ... ... }
}
List<EnvironmentMonitoringResult> misurazioniMensili = new List<EnvironmentMonitoringResult>();
myPredicate ricercaCaso1 = new myPredicate(CostructormanagerType.gasButano, false);
EnvironmentMonitoringResult controlloAllarmeNotturno = misurazioniMensili.Find(ricercaCaso1.findTipo1);
EnvironmentMonitoringResult controlloCrashZonaAlfaX = misurazioniMensili.Find(ricercaCaso1.findTipo2);
You can see that even complex searches can be enclosed in a single class and maybe implemented in a separate layer (e.g., EnvironmentService
), separate from any interface
or BusinessLogic
.
Points of Interest
Just as we used anonymous delegates for the Find()
method and FindAll()
method, we can expand our class by implementing the IComparable interface and use the predicates (or Lambda expressions) for the Sort()
method too.