Cómo hacer funciones en línea en C #

98

Estoy usando Linq To XML

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

pero quiero hacer algunos cálculos con el prefijo antes de agregarlo al xml, como eliminar espacios, caracteres especiales, algunos cálculos, etc.

No quiero crear funciones porque estas no serán de ninguna ayuda en ninguna otra parte de mi programa, pero esto, ¿hay alguna forma de crear funciones en línea?

Joe Dixon
fuente
5
No se prohíba crear una función solo porque no se llamará desde ningún otro lugar. Las funciones también se pueden utilizar para limpiar el código (si tiene un gran proceso que abarca varias páginas rompiéndolo en funciones hace que sea más fácil de leer) y documento independiente (solo nombrar una función correctamente puede transmitir información al lector.)
Joe
14
Si vino aquí desde Google y desea saber cómo emular funciones en línea en c #, vaya aquí: stackoverflow.com/questions/473782/inline-functions-in-c Las respuestas a continuación no pertenecen a funciones "en línea" en el verdadero sentido de la palabra, y c # ni siquiera tiene funciones en línea verdaderas, pero el enlace anterior proporciona una optimización del compilador que se puede usar.
JamesHoux
Gracias James, el título de la pregunta es bastante engañoso.
Zar Shardan

Respuestas:

227

Sí, C # lo admite. Hay varias sintaxis disponibles.

  • Se agregaron métodos anónimos en 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!");
    }
    
  • Las lambdas son nuevas en C # 3.0 y vienen en dos sabores.

    • Lambdas de expresión:

      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
      
    • Lambdas de declaración:

      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; };
      
  • Las funciones locales se han introducido con C # 7.0:

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

Básicamente, existen dos tipos diferentes de estos: Funcy Action. Funcs devuelven valores pero Actionno. El último parámetro de tipo de a Funces el tipo de retorno; todos los demás son tipos de parámetros.

Hay tipos similares con diferentes nombres, pero la sintaxis para declararlos en línea es la misma. Un ejemplo de esto es Comparison<T>, que es 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

Estos se pueden invocar directamente como si fueran 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.
R. Martinho Fernandes
fuente
4
No entiendo esta respuesta. En su ejemplo, quiere un nuevo XElement ("Prefijo", prefijo => newPrefix) declarado en línea. Todas estas respuestas equivalen a declarar una nueva función. ¿Por qué no declarar una nueva función?
Matthew James Davis
3
porque: 1. comprender la lógica del método, leer la función en línea es mucho más fácil que buscar diferentes declaraciones 2. con frecuencia es necesario escribir menos código cuando se usan funciones en línea
Volodymyr
35

C # 7 agrega soporte para funciones locales

Aquí está el ejemplo anterior usando una función local

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

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

La respuesta a su pregunta es sí y no, dependiendo de lo que quiera decir con "función en línea". Si está usando el término como se usa en el desarrollo de C ++, entonces la respuesta es no, no puede hacer eso, incluso una expresión lambda es una llamada a función. Si bien es cierto que puede definir expresiones lambda en línea para reemplazar declaraciones de funciones en C #, el compilador aún termina creando una función anónima.

Aquí hay un código realmente simple que usé para probar esto (VS2015):

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

¿Qué genera el compilador? Usé una ingeniosa herramienta llamada ILSpy que muestra el ensamblaje de IL real generado. Eche un vistazo (he omitido muchas cosas de configuración de clases)

Esta es la función 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

¿Ves esas líneas IL_0026 e IL_0027? Esas dos instrucciones cargan el número 5 y llaman a una función. Luego, formatee IL_0031 e IL_0036 e imprima el resultado.

Y aquí está la función llamada:

        .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'

Es una función realmente corta, pero es una función.

¿Vale la pena hacer algún esfuerzo para optimizarlo? Nah. Tal vez si lo llama miles de veces por segundo, pero si el rendimiento es tan importante, entonces debería considerar llamar al código nativo escrito en C / C ++ para que haga el trabajo.

En mi experiencia, la legibilidad y la capacidad de mantenimiento son casi siempre más importantes que la optimización para obtener unos pocos microsegundos de ganancia de velocidad. Utilice funciones para hacer que su código sea legible y para controlar el alcance de las variables y no se preocupe por el rendimiento.

"La optimización prematura es la raíz de todos los males (o al menos la mayor parte) en la programación". - Donald Knuth

"Un programa que no se ejecuta correctamente no necesita ejecutarse rápido" - Yo

Ray Fischer
fuente
3
Este es uno de los principales éxitos de Google para incorporar funciones de C #. Dices "Quizás si lo llamas miles de veces por segundo", y adivina qué, lo llamo 4 millones de veces por segundo, así que sí ... la inserción probablemente sería útil (y aumentaría enormemente la legibilidad del código frente a lo que Estoy a punto de tener que hacer: desenrollado manual de una función en 16 lugares).
Adam
17

Si.

Puede crear métodos anónimos o expresiones lambda :

Func<string, string> PrefixTrimmer = delegate(string x) {
    return x ?? "";
};
Func<string, string> PrefixTrimmer = x => x ?? "";
SLaks
fuente
Gracias por tu respuesta, ¿podrías desarrollar un poco más la respuesta, la lamba, me refiero a codeFunc <string, string> PrefixTrimmer = x => x? ""; codeSoy nuevo en esto y realmente no entiendo muy bien el documento de MSDN
Joe Dixon
2
@Joe: x ?? ""es el operador de fusión nula. msdn.microsoft.com/en-us/library/ms173224.aspx
SLaks
In x => (something), xes un parámetro y (something)es el valor de retorno`.
SLaks
2
@Hellfrost: Lee la pregunta. No pregunta sobre funciones en línea de estilo C ++. No quiero crear funciones porque estas no serán de ninguna ayuda en ninguna otra parte de mi programa, pero esto
SLaks
5

Puede usar Func, que encapsula un método que tiene un parámetro y devuelve un valor del tipo especificado por el parámetro TResult.

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


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

No solo los métodos Inside, también se pueden usar dentro de las clases.

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);
    }
Deepak Mishra
fuente