Como fazer funções inline em C #


102

Estou usando Linq To XML

new XElement("Prefix", Prefix == null ? "" : Prefix)

mas eu quero fazer alguns cálculos para o prefixo antes de adicioná-lo ao xml, como eliminar espaços, caracteres especiais, alguns cálculos etc.

Eu não quero criar funções porque estas não vão ajudar em nenhuma outra parte do meu programa além dessa, então há alguma maneira de criar funções inline ??


5
Não se proíba de criar uma função só porque ela não será chamada de nenhum outro lugar. As funções também podem ser usadas para limpar o código (se você tiver um procedimento grande abrangendo várias páginas, dividi-lo em funções torna-o mais legível) e autodocumentar (apenas nomear uma função corretamente pode transmitir informações ao leitor.)
Joe

14
Se você veio do Google e deseja saber como emular funções inline em c #, clique aqui: stackoverflow.com/questions/473782/inline-functions-in-c As respostas abaixo não se referem a funções "inline" no verdadeiro sentido da palavra, e c # nem mesmo tem verdadeiras funções embutidas, mas o link acima fornece uma otimização do compilador que pode ser usada.
JamesHoux,

Obrigado, James, o título da pergunta é bastante enganador.
Zar Shardan

Respostas:


237

Sim, C # suporta isso. Existem várias sintaxes disponíveis.

  • Métodos anônimos foram adicionados em C # 2.0:

    Func<int, int, int> add = delegate(int x, int y)
    {
        return x + y;
    };
    Action<int> print = delegate(int x)
    {
        Console.WriteLine(x);
    }
    Action<int> helloWorld = delegate // parameters can be elided if ignored
    {
        Console.WriteLine("Hello world!");
    }
    
  • Lambdas são novos no C # 3.0 e vêm em dois sabores.

    • Expressão lambdas:

      Func<int, int, int> add = (int x, int y) => x + y; // or...
      Func<int, int, int> add = (x, y) => x + y; // types are inferred by the compiler
      
    • Declaração lambdas:

      Action<int> print = (int x) => { Console.WriteLine(x); };
      Action<int> print = x => { Console.WriteLine(x); }; // inferred types
      Func<int, int, int> add = (x, y) => { return x + y; };
      
  • As funções locais foram introduzidas com C # 7.0:

    int add(int x, int y) => x + y;
    void print(int x) { Console.WriteLine(x); }
    

Existem basicamente dois tipos diferentes para estes: Funce Action. Funcs retornam valores, mas Actions não. O último parâmetro de tipo de a Funcé o tipo de retorno; todos os outros são os tipos de parâmetro.

Existem tipos semelhantes com nomes diferentes, mas a sintaxe para declará-los sequencialmente é a mesma. Um exemplo disso é Comparison<T>, que é aproximadamente equivalente a Func<T, T, int>.

Func<string, string, int> compare1 = (l,r) => 1;
Comparison<string> compare2 = (l, r) => 1;
Comparison<string> compare3 = compare1; // this one only works from C# 4.0 onwards

Eles podem ser invocados diretamente como se fossem métodos regulares:

int x = add(23, 17); // x == 40
print(x); // outputs 40
helloWorld(x); // helloWorld has one int parameter declared: Action<int>
               // even though it does not make any use of it.

4
Eu não entendo essa resposta. Em seu exemplo, ele deseja um novo XElement ("Prefix", prefix => newPrefix) declarado embutido. Todas essas respostas equivalem a declarar uma nova função. Por que não apenas declarar uma nova função?
Matthew James Davis

3
porque: 1. compreender a lógica da função inline de leitura de método é muito mais fácil do que pesquisar declarações diferentes 2. frequentemente você precisa escrever menos código ao usar funções inline
Volodymyr

37

C # 7 adiciona suporte para funções locais

Aqui está o exemplo anterior usando uma função local

void Method()
{
    string localFunction(string source)
    {
        // add your functionality here
        return source ;
    };

   // call the inline function
   localFunction("prefix");
}

30

A resposta à sua pergunta é sim e não, dependendo do que você entende por "função embutida". Se você estiver usando o termo como é usado no desenvolvimento C ++, a resposta é não, você não pode fazer isso - mesmo uma expressão lambda é uma chamada de função. Embora seja verdade que você pode definir expressões lambda in-line para substituir as declarações de função em C #, o compilador ainda acaba criando uma função anônima.

Aqui está um código muito simples que usei para testar isso (VS2015):

    static void Main(string[] args)
    {
        Func<int, int> incr = a => a + 1;
        Console.WriteLine($"P1 = {incr(5)}");
    }

O que o compilador gera? Eu usei uma ferramenta bacana chamada ILSpy que mostra o assembly real do IL gerado. Dê uma olhada (omiti muitas coisas de configuração de classe)

Esta é a função principal:

        IL_001f: stloc.0
        IL_0020: ldstr "P1 = {0}"
        IL_0025: ldloc.0
        IL_0026: ldc.i4.5
        IL_0027: callvirt instance !1 class [mscorlib]System.Func`2<int32, int32>::Invoke(!0)
        IL_002c: box [mscorlib]System.Int32
        IL_0031: call string [mscorlib]System.String::Format(string, object)
        IL_0036: call void [mscorlib]System.Console::WriteLine(string)
        IL_003b: ret

Vê essas linhas IL_0026 e IL_0027? Essas duas instruções carregam o número 5 e chamam uma função. Então IL_0031 e IL_0036 formatam e imprimem o resultado.

E aqui está a função chamada:

        .method assembly hidebysig 
            instance int32 '<Main>b__0_0' (
                int32 a
            ) cil managed 
        {
            // Method begins at RVA 0x20ac
            // Code size 4 (0x4)
            .maxstack 8

            IL_0000: ldarg.1
            IL_0001: ldc.i4.1
            IL_0002: add
            IL_0003: ret
        } // end of method '<>c'::'<Main>b__0_0'

É uma função muito curta, mas é uma função.

Vale a pena fazer algum esforço para otimizar? Nah. Talvez se você estiver chamando isso milhares de vezes por segundo, mas se o desempenho for tão importante, você deve considerar chamar o código nativo escrito em C / C ++ para fazer o trabalho.

Na minha experiência, a legibilidade e a manutenção são quase sempre mais importantes do que otimizar o ganho de velocidade de alguns microssegundos. Use funções para tornar seu código legível e para controlar o escopo de variáveis ​​e não se preocupe com o desempenho.

"Otimização prematura é a raiz de todos os males (ou pelo menos a maioria deles) na programação." - Donald Knuth

"Um programa que não funciona corretamente não precisa ser executado rápido" - Eu


4
Este é um dos principais sucessos do Google para funções C # embutidas. Você diz "Talvez se você estiver chamando milhares de vezes por segundo", e adivinhe - estou chamando 4 milhões de vezes por segundo, então sim ... inlining provavelmente seria útil (e aumentaria enormemente a legibilidade do código em comparação com o que Estou prestes a fazer: desenrolamento manual de uma função em 16 lugares).
Adam

Para aqueles familiarizados com o Kotlin, isso significa que as inlinefunções do Kotlin não têm equivalente em C #.
Vapid Linus

17

Sim.

Você pode criar métodos anônimos ou expressões lambda :

Func<string, string> PrefixTrimmer = delegate(string x) {
    return x ?? "";
};
Func<string, string> PrefixTrimmer = x => x ?? "";

Obrigado pela sua resposta, você poderia elaborar um pouco mais sobre a resposta, a lamba, quero dizer, o codeFunc <string, string> PrefixTrimmer = x => x ?? ""; codeSou novo nisso e realmente não entendo muito bem o documento do MSDN
Joe Dixon

2
@Joe: x ?? ""é o operador de coalescência nula. msdn.microsoft.com/en-us/library/ms173224.aspx
SLaks

In x => (something), xé um parâmetro e (something)é o valor de retorno`.
SLaks


2
@Hellfrost: Leia a pergunta. Ele não está perguntando sobre funções embutidas no estilo C ++. Não quero criar funções porque estas não ajudarão em nenhuma outra parte do meu programa, exceto esta
SLaks

5

Você pode usar Func, que encapsula um método que possui um parâmetro e retorna um valor do tipo especificado pelo parâmetro TResult.

void Method()
{
    Func<string,string> inlineFunction = source => 
    {
        // add your functionality here
        return source ;
     };


    // call the inline function
   inlineFunction("prefix");
}

4

Não apenas os métodos Inside, mas também podem ser usados ​​dentro das classes.

class Calculator
    {
        public static int Sum(int x,int y) => x + y;
        public static Func<int, int, int>  Add = (x, y) => x + y;
        public static Action<int,int> DisplaySum = (x, y) => Console.WriteLine(x + y);
    }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.