How to declare a function within another function in C#?

Asked

Viewed 1,094 times

8

In Delphi it is possible to declare function within the block of a given function, see this example done in Delphi:

procedure TForm1.btnRunClick(Sender: TObject);
begin
  mostrarNome('Carvalho');
end;

function TForm1.mostrarNome(nome: String): String;
  function transformaMaiuscula(str: String): String;
  begin
    Result := UpperCase(str);    
  end;
begin
  ShowMessage(transformaMaiuscula(nome));
end;

The exit will be:

CARVALHO //In capital letters

Therefore, I would like to know if it is possible to do the same in C#, declare a function within another function. See this code in C#:

class Program
{
    static void Main(string[] args)
    {
        mostraNome("Carvalho");

        Console.ReadKey();
    }

    static void mostraNome(string nome) 
    {
        static string transformaMaiuscula(string str)
        {
            return str.ToUpper();
        }

        Console.WriteLine(transformaMaiuscula());
    }
}

The above C# code does not work, the compiler says the syntax is invalid, however, it only serves as an illustration.

There’s a way to do this in C#?

2 answers

8


Gambiarra

Until C#6 the only way was the use of lambda which is nothing more than a pointer to function (can eventually store captured variables as well), so it is possible to write the body of a function and store in a variable or pass as argument, or still return in the function that has been declared.

Func<string string> transformaMaiuscula = (string str) => str.ToUpper();
transformaMaiuscula("Teste"); //obviamente pode fazer o que quiser

This feature is a syntactic sugar upon delegates.

It exists since C# 3. In C# 2 there was the syntax of delegate which is similar to the syntax of lambda, but less convenient. In C# 1 there was a delegate, but the syntax was so inconvenient that it had to declare the delegate and then declare and define a separate method, that is, nor could it declare within another method.

Func<string string> transformaMaiuscula = delegate(string str) {
    return str.ToUpper();
};
Console.WriteLine(transformaMaiuscula(nome));

That one Func<string string> is a delegate already declared by . NET, for your convenience. In other circumstances also could use a Action and very possibly a Predicate. Only the Predicate existed in C# 2, which was quite limiting, and which required in practice to declare the delegate outside the method.

I won’t even use the C# example because it doesn’t even look like what’s being asked for, but the delegate’s statement would be delegate string transformaMaiuscula(string str), then declare the method with the same signature. The declaration goes inside a type, but not inside a method.

Note that using a delegate with syntax of lambda is quite different from using a normal method. You can even simulate this idea, but they have different characteristics. And it’s a tremendous gambit to do this. Even has memory consumption, processing expense and possible loss of reliability in more complex cases if the programmer does not take certain care.

If you need a delegate there is fine, pay the price of using it. It is not the case of the question.

Syntax lambda out of method

C# 6 even allowed the syntax of Amble is used in any method, so when the method is very simple you can declare your body in a simpler way. It now has the syntax of lamba, but not its semantics. It was only possible to use in normal methods of the types.

string transformaMaiuscula(string str) => str.ToUpper();

Not that this helps the question, I put as a curiosity because people take a long time to get used to new features.

Correct solution

Then one might think: will one do what one has nothing else? Well, I would not use one lambda when only a local (nested) function is desired, as the question shows. My solution, and this is the official recommendation, is to simply deconstruct the function. Just create a separate, probably private, static method and call it where you need it.

It only has one drawback, other methods of the class may call it. But it is something manageable and does not generate any other cost.

private static string transformaMaiuscula(string str) => str.ToUpper();

There inside your method simply calls the method transformaMaiuscula() like any other.

C# 7

In C# 7 it is already possible to use the local function syntax, so it does not pay any price and is not at risk, not even to allow the call of this function by other members of the class.

The syntax is almost the one used by AP:

static void mostraNome(string nome) {
    static string transformaMaiuscula(string str) {
        return str.ToUpper();
    }
    Console.WriteLine(transformaMaiuscula(nome));
}

The static allowed in syntax, it gives more security to avoid improper captures of variables in the larger context.

Note that you can declare the local method after using it. It is not possible to do this with lambda, the statement must come before always.

Simply put, I could use it like this:

static void mostraNome(string nome) {
    static string transformaMaiuscula(string str) => str.ToUpper();
    Console.WriteLine(transformaMaiuscula(nome));
}

I put in the Github for future reference.

7

Directly, no. But you can declare a Action or delegate, or Func<>, which is almost the same thing.

using System;

public class Program
{
    delegate void Teste2();

    public static void Main()
    {
        Action Teste = () =>
        {
            Console.WriteLine("Teste!");
        };

        Teste2 teste2 = delegate() {
            Console.WriteLine("Teste 2!");
        };

        Func<string, string> teste3 = delegate(string argumento) {
            return argumento + " " + argumento;
        };

        Teste();
        teste2();
        Console.WriteLine(teste3("Buemba!"));
        Console.WriteLine("Hello World");
    }
}

I made a Fiddle.

Upshot:

Teste!
Teste 2!
Buemba! Buemba!
Hello World

The difference between them is that delegate needs to be strongly typed. Action is a little more dynamic, but does not return any value. To return values, use Func<TArgumentos, TRetorno>, where TRetorno is what will be returned and TArgumentos are the types of arguments.

  • It is possible for me to specify a parameter and a return to the Action?

  • 1

    Yes, I’ll edit with one more example.

  • I guess you got it all now.

  • Yes, but I’m going to ask another question about Func<>.

  • Okay, make yourself at home :)

  • 4

    Just for the record, it already has an approved proposal for C# 7 that works precisely with the idea that the question had, it is called "Nested local Function". https://github.com/dotnet/roslyn/issues/259

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.