Discussion
One of the best ways we’ve heard of to describe closures in C# is to think of an
object as a set of methods associated with data and to think of a closure as a set of
data associated with a function. If you need to have several different operations on
the same data, an object approach may make more sense. These are two different
angles on the same problem, and the type of problem you are solving will help you
decide which is the right approach. It just depends on your inclination as to which
way to go. There are times when one-hundred-percent pure object-oriented pro-
gramming can get tedious and is unnecessary, and closures are a nice way to solve
some of those problems. The SalesWeasel commission example presented here is a
demonstration of what you can do with closures. It could have been done without
them, but at the expense of writing more class and method code.
Closures have been defined as stated earlier, but there is a stricter definition that
essentially implies that the behavior associated with the state should not be able to
modify the state in order to be a true closure. We tend to agree more with the first
definition, as it defines what a closure should be, not how it should be implemented,
which seems too restrictive. Whether you choose to think of this as a neat side fea-
ture of lambda expressions or you feel it is worthy of being called a closure, it is
another programming trick for your toolbox and should not be dismissed.
See Also
Recipe 9.11; the “Lambda Expressions” topic in the MSDN documentation.
9.11 Performing Multiple Operations on a List Using
Functors
Problem
You want to be able to perform multiple operations on an entire collection of objects
at once, while keeping the operations functionally segmented.
Solution
Use a functor (or function object, as it is also known) as the vehicle for transforming
the collection. A functor is any object that can be called as a function. Examples of
this are a delegate, a function, a function pointer, or even an object that defines
operator( ) for us C/C++ converts.
Needing to perform multiple operations on a collection is a reasonably common
thing in software. Let’s say that you have a stock portfolio with a bunch of stocks in
it. Your StockPortfolio class would have a List of Stock object and would be able to
add stocks:
Performing Multiple Operations on a List Using Functors | 361
Superset
(A ⊃ B)
Returns true if all elements of set A are contained in a second set B; otherwise, it
returns false. Set A may contain elements not found in B.
Equivalence
(A == B)
Returns true if both HashSet<T> objects contain the same number of elements
and the same value for each element; otherwise, it returns false. This is equiva-
lent to stating that (A ⊂ B) and (B ⊂ A).
The following code creates and populates two HashSet<T> objects:
HashSet<int> set1 = new HashSet<int>( );
HashSet<int> set2 = new HashSet<int>( );
set1.Add(1);
set1.Add(2);
set1.Add(3);
set1.Add(4);
set1.Add(5);
set1.Add(6);
set2.Add(-10);
set2.Add(2);
set2.Add(40);
The union operation can be performed by using the UnionWith method and passing in
a HashSet<T> with which to union the current HashSet<T>. Essentially, the resulting
set contains elements that exist in either of the two HashSet<T> objects or both
HashSet<T> objects. The following code demonstrates the union operation:
set1.UnionWith(set2);
The intersection operation is set up similarly to the union operation. To perform an
intersection between two HashSet<T> objects, use the IntersectWith method. Essen-
tially, an element must be in both HashSet<T> A and HashSet<T> B in order for it to be
placed in the resulting HashSet<T> object. The following code demonstrates the inter-
section operation:
set1.IntersectionOf(set2);
The difference operation is performed through the SymmetricExceptWith method.
Essentially, only elements in either set, but not both, are placed in the resulting set.
The following code demonstrates the difference operation:
set1.SymmetricExceptWith(set2);
Using a HashSet Object | 447
and why the stack is overflowing or potential trouble spots where the stack may
grow very large.
It is very easy to obtain a stack trace using the System.Environment.StackTrace prop-
erty. Unfortunately, this stack trace also lists three methods defined in the System.
Environment class that are called when you use the Environment.StackTrace property.
The returned stack trace, using this method, will look something like following:
at System.Environment.GetStackTrace(Exception e)
at System.Environment.GetStackTrace(Exception e)
at System.Environment.get_StackTrace( )
at Chapter_Code.Class1.ObtainingStackTrace( ) in c:\book cs cookbook\test.cs:line
260
at Chapter_Code.Class1.Main(String[] args) in c:\book cs cookbook\main.cs:line 78
The first three items in the stack trace are method calls that you are not interested in.
To fix this, you can write the following method to find and remove these items from
the stack trace:
public static string GetStackTraceInfo(string currentStackTrace)
{
string firstStackTraceCall = "System.Environment.get_StackTrace( )";
int posOfStackTraceCall =
currentStackTrace.IndexOf(firstStackTraceCall,StringComparison.
OrdinalIgnoreCase);
return (currentStackTrace.Substring(posOfStackTraceCall +
firstStackTraceCall.Length));
}
This method is called using the following line of code:
string stackTraceInfo = GetStackTraceInfo(System.Environment.StackTrace);
The second line in the GetStackTraceInfo method creates and initializes a string vari-
able to the first called StackTrace method—which is actually a call to the get portion
of the StackTrace property. This variable is used in the third line to obtain its start-
ing position in the complete stack trace string. The final line of code grabs the end of
the complete stack trace string, starting at the ending of the first called StackTrace
method. The FinalStackTrace variable now contains the following string:
at Chapter_Code.Class1.ObtainingStackTrace( ) in c:\book cs cookbook\test.cs:line
260
at Chapter_Code.Class1.Main(String[] args) in c:\book cs cookbook\main.cs:line 78
This is the current stack trace at the point in the code where the Environment.
StackTrace method was called.
Now that you have a stack trace of your code, you can calculate the stack depth at
the point where you call Environment.StackTrace. The following code uses a regular
expression to determine the depth of a stack trace:
using System;
using System.Text.RegularExpressions;
270 | Chapter 7: Exception Handling
Console.WriteLine(item);
}
}
These next three methods return an object instead of simply a string. The first
instance method is GetFileSystemInfos, which returns both directories and files in a
single directory that match pattern:
public static void DisplayDirectoryContentsWithPattern(string path,
string pattern)
{
DirectoryInfo mainDir = new DirectoryInfo(path);
IEnumerable<string> fileSystemDisplayInfos =
from fsi in mainDir.GetFileSystemInfos(pattern)
where fsi is FileSystemInfo || fsi is DirectoryInfo
select fsi.ToDisplayString( );
foreach (string s in fileSystemDisplayInfos)
{
Console.WriteLine(s);
}
}
The GetDirectories instance method returns only directories (contained in the
DirectoryInfo object) in a single directory that match pattern:
public static void DisplayDirectories(string path, string pattern)
{
DirectoryInfo mainDir = new DirectoryInfo(@"C:\TEMP ");
DirectoryInfo[] items = mainDir.GetDirectories(pattern);
foreach (DirectoryInfo item in items)
{
Console.WriteLine("DIRECTORY: " + item.Name); }
}
The GetFiles instance method returns only file information (contained in the
FileInfo object) in a single directory that matches pattern:
public static void DisplayFiles(string path, string pattern)
{
DirectoryInfo mainDir = new DirectoryInfo(@"C:\TEMP ");
FileInfo[] items = mainDir.GetFiles(pattern);
foreach (FileInfo item in items)
{
Console.WriteLine("FILE: " + item.Name);
}
}
Discussion
If you need just an array of strings containing paths to both directories and files, you
can use the static method Directory.GetFileSystemEntries. The string array returned
does not include any information about whether an individual element is a directory
462 | Chapter 12: Filesystem I/O