Як зробити вбудовані функції в C #


97

Я використовую Linq To XML

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

але я хочу зробити обчислення префікса перед тим, як додати його до xml, наприклад, усунути пробіли, спеціальні символи, деякі обчислення тощо

Я не хочу створювати функції, тому що ці не допоможуть жодній іншій частині моєї програми, але це, то чи є спосіб створити вбудовані функції?


5
Не забороняйте собі створювати функцію лише тому, що її не буде викликано з іншого місця. Функції також можна використовувати для очищення коду (якщо у вас велика процедура, що охоплює кілька сторінок, розбиття на функції робить його більш читабельним) та самодокументування (просто належне називання функції може передати інформацію читачеві.)
Джо

14
Якщо ви прийшли сюди з Google і хочете знати, як емулювати вбудовані функції в c #, перейдіть сюди: stackoverflow.com/questions/473782/inline-functions-in-c Наведені нижче відповіді не стосуються "вбудованих" функцій у справжньому розумінні цього слова, а c # навіть не має справжніх вбудованих функцій, але посилання вище забезпечує оптимізацію компілятора, яку можна використовувати.
JamesHoux,

Дякую Джеймсу, назва запитання справді досить оманлива.
Зар Шардан

Відповіді:


226

Так, C # це підтримує. Доступно кілька синтаксисів.

  • У 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!");
    }
    
  • Лямбди є новинками в C # 3.0 і мають два варіанти.

    • Вираз лямбди:

      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
      
    • Заява лямбда:

      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; };
      
  • Локальні функції були введені з C # 7.0:

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

Є в основному два різних типи для них: Funcі Action. Funcs повертає значення, але Actions ні. Останнім параметром типу a Funcє тип повернення; всі інші - це типи параметрів.

Існують подібні типи з різними іменами, але синтаксис їх оголошення вбудованим однаковий. Прикладом цього є Comparison<T>, що приблизно еквівалентно 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

До них можна звернутися безпосередньо, як до звичайних методів:

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
Я не розумію цієї відповіді. У своєму прикладі він хоче, щоб новий XElement ("Префікс", prefix => newPrefix) був оголошений вбудованим. Усі ці відповіді означають оголошення нової функції. Чому б просто не оголосити нову функцію?
Метью Джеймс Девіс,

3
тому що: 1. зрозуміти логіку методу читання вбудованої функції набагато простіше, ніж пошук різних оголошень 2. часто вам потрібно писати менше коду при використанні вбудованих функцій
Володимир

35

C # 7 додає підтримку локальних функцій

Ось попередній приклад використання локальної функції

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

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

29

Відповідь на ваше запитання - так і ні, залежно від того, що ви маєте на увазі під "вбудованою функцією". Якщо ви використовуєте такий термін, як у розробці на C ++, тоді відповідь "ні", ви цього не можете зробити - навіть лямбда-вираз є викликом функції. Хоча це правда, що ви можете визначити вбудовані лямбда-вирази для заміни оголошень функцій у C #, компілятор все одно закінчує створенням анонімної функції.

Ось справді простий код, який я використовував для тестування (VS2015):

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

Що створює компілятор? Я використав чудовий інструмент під назвою ILSpy, який показує фактично створену збірку IL. Подивіться (я пропустив багато матеріалів про налаштування класу)

Це основна функція:

        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

Бачите ці рядки IL_0026 та IL_0027? Ці дві інструкції завантажують число 5 і викликають функцію. Потім форматуйте IL_0031 та IL_0036 та друкуйте результат.

І ось функція, що називається:

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

Це дійсно коротка функція, але це функція.

Чи варто це докладати зусиль для оптимізації? Ні Можливо, якщо ви дзвоните йому тисячі разів на секунду, але якщо продуктивність така важлива, то вам слід розглянути можливість виклику власного коду, написаного на C / C ++, щоб виконати цю роботу.

На моєму досвіді читабельність та ремонтопридатність майже завжди важливіші, ніж оптимізація швидкості на кілька мікросекунд. Використовуйте функції, щоб зробити ваш код читабельним і контролювати масштаб змінних і не турбуватися про продуктивність.

"Передчасна оптимізація - корінь усього зла (або, принаймні, більшої його частини) у програмуванні". - Дональд Кнут

"Програма, яка працює неправильно, не повинна швидко працювати" - Я


3
Це найпопулярніший хіт Google для вбудованих функцій C #. Ви кажете: "Можливо, якщо ви називаєте це тисячі разів на секунду", і вгадайте, що - я називаю це 4 мільйони разів на секунду, так що так ... вбудовування, мабуть, було б корисним (і значно збільшило би читабельність коду проти того, що Мені ось-ось доведеться зробити: розгортання однієї функції вручну в 16 місцях).
Адам

17

Так.

Ви можете створювати анонімні методи або лямбда-вирази :

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

Дякуємо за вашу відповідь, чи не могли б Ви трохи детальніше codeрозказати про відповідь, таку, як ламба, я маю на увазі Func <рядок, рядок> PrefixTrimmer = x => x ?? ""; codeя новачок у цьому, і я справді погано розумію документ MSDN
Джо Діксон,

2
@Joe: x ?? ""є оператором нулевого злиття. msdn.microsoft.com/en-us/library/ms173224.aspx
SL, 04

In x => (something), xє параметром, і (something)є значенням, що повертається`.
Слакс


2
@Hellfrost: Прочитайте питання. Він не запитує про вбудовані функції в стилі C ++. Я не хочу створювати функції, оскільки вони не допоможуть жодній іншій частині моєї програми, але це
SLaks

5

Ви можете використовувати Func, який інкапсулює метод, який має один параметр, і повертає значення типу, зазначеного параметром TResult.

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


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

4

Не тільки методи Inside, їх також можна використовувати всередині класів.

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);
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.