Search code examples
c#functional-programmingdelegatesfunc

Delegate chain method results to function body


Trying to build a formatting delegate/function. It's mainly for printing exercise results. I am trying to access the result of a argument but I think my understanding of delegates is still kind of bad. The method is invokable in the block itself but I would like the result, currently it prints the result definition. System.Func2[ConsoleApp1.LoanQueries,System.Collections.Generic.IEnumerable1[System.Decimal]]

Below is the code that will create to result to parse, the parsing method and a code snippet that I based my code on.

My questions are:

  • Is this possible or am I breaking some rules?(trying to use expressions/functional)
  • Is it possible to declare just a delegate instead of the function(This would probably involve formatting every time instead of the function.)

Create data to format

       // Method bodied expression
       public IEnumerable<decimal> LoanQueryBodied =>
            from amount in LoanAmounts
            where amount % 2 == 0
            orderby amount ascending
            select amount;

        // Query expression:
        public IEnumerable<decimal> LoanQueryExpression () =>
            LoanAmounts
                .Where(a => a % 2 == 0)
                .OrderBy(r => r);

Method for data and final formatting

public static void FormatLoans<TObject>(
            Func<TObject> instance,
            Func<TObject, IEnumerable<decimal>> function)
        {
            // This is the function object but should be IEnumerable<decimal> result passed down.
            // Just like TObject is passed , but how?
            Console.WriteLine(string.Join(" - ",function));
        }

Use method

             LoanQueries.FormatLoans<LoanQueries>(() =>
                    new LoanQueries()
                , inst => inst.LoanQueryBodied);

             LoanQueries.FormatLoans<LoanQueries>(() =>
                    new LoanQueries()
                , inst => inst.LoanQueryExpression());

Code that I based it loosely on

 public static class Disposable
    {
        public static TResult Using<TDisposable, TResult>(
            Func<TDisposable> factory,
            Func<TDisposable, TResult> map)
            where TDisposable : IDisposable
        {
            using (var disposable = factory())
            {
                return map(disposable);
            }
        }
    }

Example invoked

            var time= Disposable
                .Using(
                    () => new WebClient(),
                    client => XDocument.Parse(client.DownloadString("http://time.gov/actualtime.cgi")))
                .Root
                .Attribute("time")
                .Value;

I would like to chain my method like this but if this is not possible or bad practice I would also like to know.


Solution

  • You need to call function and instance using ():

    Console.WriteLine(string.Join(" - ",function(instance())));
    

    And apparently you want to return the string, rather than printing it, to allow chaining, so:

    public static string FormatLoans<TObject>(
            Func<TObject> instance,
            Func<TObject, IEnumerable<decimal>> function) =>
        string.Join(" - ",function(instance()));
    

    However, I think you are really over-complicating/over-generalising this. Your method is not nearly as general as the Disposable.Using method you showed. Your method could just be declared like this:

    public static string FormatLoans(IEnumerable<decimal> loans) =>
        string.Join(" - ", loans);
    

    Callers:

    LoanQueries.FormatLoans(new LoanQueries().LoanQueryBodied)
    LoanQueries.FormatLoans(new LoanQueries().LoanQueryExpression)
    

    Disposable.Using uses delegates because it is trying to recreate the using statement. The second argument must be evaluated inside the using statement, so that any exceptions thrown lead to the disposal of the IDisposable, which is why it has to be wrapped up in a delegate. If not, the second argument would be evaluated before the Using method runs, and that misses the whole point.

    However, your method does not have special requirements like that, so you don't need delegates.