简体   繁体   中英

When should I use C# Generic Func<T,Tresult>?

I have been looking at C#'s Generic function delegates (Func) feature.

Example:

// Instantiate delegate to reference UppercaseString method
Func<string, string> convertMethod = UppercaseString;
string name = "Dakota";

// Use delegate instance to call UppercaseString method
Console.WriteLine(convertMethod(name));

I'm struggling to think of a real life scenario where they might be useful in my own applications. So I thought I would put the question out there. \\

I'd greatly appreciate your thoughts.

Say one needs to time method calls for reporting purposes. Placing the timing code inside the function is not feasible for maintenance reasons.

So by using the Func call one can do the time operation and not interfere:

static void Time<T>(Func<T> work)
{
    var sw = Stopwatch.StartNew();
    var result = work();
    Console.WriteLine(sw.Elapsed + ": " + result);
}

Then call it with our function to be timed

Time(() => { return "Jabberwocky"; });

Result:

 00:00:00.0000926: Jabberwocky

Here is an example use of Funct<T,TResult> using the same time motif to time regex vs string.split

var strData = "The rain in Spain";

Time((str) => { return Regex.Split(str, @"\s"); }, strData);
Time((str) => { return str.Split(); },             strData);

Here is the setup

static void Time<T,S>(Func<T,S> work, T strToSplit)
{
   var sw = Stopwatch.StartNew();
   var result = work(strToSplit);
   sw.Stop();

    var joined = string.Join(", ", result as string[]);
    Console.WriteLine("{0} : {1}", sw.Elapsed, joined);

}

And the results

00:00:00.0000232 : The, rain, in, Spain
00:00:00.0000021 : The, rain, in, Spain

Updating this answer for 2017. This is not a Func but its non return sibling Action ; I have found I do a basic form of logging on my classes where I use Dependency Injection from a consumer such as this:

Action<string> ReportIt  { get; set; }

public void ReportStatus(Action<string> statusReporter)
{
    ReportIt = statusReporter;   
}

The idea is that status reporting is optional, so later in the code I check to see if it is vialble and if so I give status:

 ReportIt?.Invoke("Running - Connection initiated");

The consumer of the class calls it such as

 piperInstance.ReportStatus( Console.WriteLine );

which could also be expressed as

 piperInstance.ReportStatus((str) => { Console.WriteLine(str); } );

A bit long for a comment (making a community wiki)

The best way to explain the use case is for the fucntional map function.

In C# the is called Select .

Say you have a list a strings, map will allow you to to change the strings.

As in you example:

somewords.Select(Uppercase) or

somewords.Select(x = Uppercase(x)) or

somewords.Select(x => x.ToUpper())

But it is not limited to string to string conversions. Say you want to get a list of the of the length of all string, you can simply do:

somewords.Select(x => x.Length)

Above will return a list of integers (or whatever is used in your language).

Once you start combining the this with other higher order functions, things become fun :)

Going back to the previous example, but say you want the length of the longest string. You have many options to do it:

somewords.Select(x => x.Length).Max()
somewords.OrderByDescending(x => x.Length).First()
// or even
somewords.Max(x => x.Length)

And probably a few more I missed. In the end it all helps you expressing your intent without having to do a lot of extra work (like the last example).

Note:

In every case above, x => x.Length could have been replaced by any function that did the same as getting the length, say Foo . Eg

int Foo(string s)
{
  return s.Length;
}

So somewords.Max(Foo) is the same.

Saw an interesting real-life example just now:

public static List<Transaction> ReadAll(Settings settings, Func<string, FileInfo[]> getFilesOfType)
{
     FileInfo[] files = getFilesOfType("CashRevenue");
     // does stuff with files
}

which is called by

var transactions = FileParser.ReadAll(m_status.FilesByType)

which provides the function FilesByType as a parameter

public FileInfo[] FilesByType (string type)
{
     return result = Files.Where(f => f.Type == type)
                 .Select(f => new FileInfo(f.LocalPath))
                 .ToArray();
}

where Files is a collection property which can be filtered with the string parameter to FilesByType

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM