C # 'da satır içi işlevler nasıl yapılır


98

Linq To XML kullanıyorum

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

ancak öneki xml'ye eklemeden önce boşlukları, özel karakterleri, bazı hesaplamaları vb. ortadan kaldırmak gibi bazı hesaplamalar yapmak istiyorum.

İşlevler oluşturmak istemiyorum çünkü bu işlevler programımın başka bir bölümünde yardımcı olmayacak ama bu, dolayısıyla satır içi işlevler oluşturmanın herhangi bir yolu var mı?


5
Sırf başka bir yerden çağrılmayacağı için bir işlev oluşturmaktan kendinizi yasaklamayın. İşlevler, kodu temizlemek için de kullanılabilir (birkaç sayfaya yayılan büyük bir prosedürünüz onu işlevlere bölerek daha okunaklı hale getirir) ve kendi kendine belge (sadece bir işlevi doğru şekilde adlandırmak okuyucuya bilgileri iletebilir.)
Joe

14
Buraya Google'dan geldiyseniz ve c #'da satır içi işlevlerin nasıl taklit edileceğini öğrenmek istiyorsanız, buraya gidin: stackoverflow.com/questions/473782/inline-functions-in-c Aşağıdaki yanıtlar "satır içi" işlevlerle ilgili değildir kelimenin tam anlamıyla, ve c # gerçek satır içi işlevlere bile sahip değildir, ancak yukarıdaki bağlantı kullanılabilecek bir derleyici optimizasyonu sağlar.
JamesHoux 21

Teşekkürler James, soru başlığı aslında oldukça yanıltıcı.
Zar Shardan

Yanıtlar:


228

Evet, C # bunu destekler. Kullanılabilen birkaç sözdizimi vardır.

  • Anonim yöntemler C # 2.0'da eklendi:

    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!");
    }
    
  • Lambdalar , C # 3.0'da yenidir ve iki farklı tada sahiptir .

    • İfade 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
      
    • İfade 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; };
      
  • Yerel işlevler C # 7.0 ile tanıtıldı:

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

Bunlar için temelde iki farklı türü vardır: Funcve Action. Funcdeğerleri döndürür ancak s döndürmez Action. A'nın son tür parametresi Funcdönüş türüdür; diğerleri parametre türleridir.

Farklı adlara sahip benzer türler vardır, ancak bunları satır içi olarak bildirmenin sözdizimi aynıdır. Bunun bir örneği Comparison<T>, kabaca eşdeğer olan 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

Bunlar, sanki normal yöntemlermiş gibi doğrudan çağrılabilir:

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
Bu cevabı anlamıyorum. Örneğinde, yeni XElement'in ("Önek", önek => newPrefix) satır içi olarak bildirilmesini istiyor. Tüm bu cevaplar yeni bir işlev ilan etmek anlamına gelir. Neden yeni bir işlev ilan etmiyorsunuz?
Matthew James Davis

3
çünkü: 1. satır içi işlev okuma yönteminin mantığını anlamak, farklı bildirimleri aramaktan çok daha kolaydır 2. satır içi işlevleri kullanırken sıklıkla daha az kod yazmanız gerekir
Volodymyr

35

C # 7 yerel işlevler için destek ekler

İşte yerel bir işlevi kullanan önceki örnek

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

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

30

"Satır içi işlev" ile ne kastettiğinize bağlı olarak sorunuzun yanıtı evet ve hayırdır. Eğer terimi C ++ geliştirmede kullanıldığı gibi kullanıyorsanız, cevap hayırdır, bunu yapamazsınız - bir lambda ifadesi bile bir işlev çağrısıdır. C # 'da işlev bildirimlerini değiştirmek için satır içi lambda ifadeleri tanımlayabileceğiniz doğru olsa da, derleyici yine de anonim bir işlev oluşturmaya devam eder.

İşte bunu test etmek için kullandığım gerçekten basit bazı kodlar (VS2015):

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

Derleyici ne üretir? Üretilen gerçek IL derlemesini gösteren ILSpy adlı şık bir araç kullandım. Bir göz atın (birçok sınıf kurulumunu ihmal ettim)

Bu Ana işlevdir:

        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 ve IL_0027 satırlarını görüyor musunuz? Bu iki talimat 5 sayısını yükler ve bir işlevi çağırır. Sonra IL_0031 ve IL_0036 biçimlendirin ve sonucu yazdırın.

Ve işte adı verilen işlev:

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

Bu gerçekten kısa bir işlev, ancak bir işlev.

Bu, optimize etmek için herhangi bir çabaya değer mi? Hayır. Belki saniyede binlerce kez çağırıyorsanız, ancak performans bu kadar önemliyse, işi yapmak için C / C ++ ile yazılmış yerel kodu çağırmayı düşünmelisiniz.

Deneyimlerime göre, okunabilirlik ve sürdürülebilirlik, birkaç mikrosaniye hız kazanımı için optimize etmekten neredeyse her zaman daha önemlidir. Kodunuzu okunabilir hale getirmek ve değişken kapsamını kontrol etmek için işlevleri kullanın ve performans konusunda endişelenmeyin.

"Erken optimizasyon, programlamadaki tüm kötülüklerin (veya en azından çoğunun) köküdür." - Donald Knuth

"Düzgün çalışmayan bir programın hızlı çalışması gerekmez" - Me


4
Bu, C # işlevlerini sıralamak için en iyi Google hitidir. "Belki saniyede binlerce kez çağırırsanız" diyorsunuz ve tahmin edin - saniyede 4 milyon kez arıyorum, bu yüzden evet ... satır içi yapmak muhtemelen yararlı olacaktır (ve kod okunabilirliğini büyük ölçüde artıracaktır. Yapmak üzereyim: 16 yerde bir işlevin manuel olarak açılması).
Adam

Kotlin'e aşina olanlar için bu, Kotlin'in inlineişlevlerinin C #'da karşılığı olmadığı anlamına gelir .
Vapid Linus

17

Evet.

Oluşturabilir anonim yöntemleri veya lambda ifadeleri :

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

Cevabınız için teşekkürler, cevabı biraz daha detaylandırır mısınız, lamba bir, yani codeFunc <string, string> PrefixTrimmer = x => x ?? ""; codebu konuda yeniyim ve MSDN belgesini gerçekten çok iyi anlamıyorum
Joe Dixon

2
@ Joe: x ?? ""boş birleştirme operatörüdür. msdn.microsoft.com/en-us/library/ms173224.aspx
SLaks

İçinde x => (something), xbir parametredir ve (something)dönüş değeridir`.
SLaks


2
@Hellfrost: Soruyu okuyun. C ++ tarzı satır içi işlevler hakkında soru sormuyor. İşlevler oluşturmak istemiyorum çünkü bunlar programımın başka bir bölümünde yardımcı olmayacak ama bu
SLaks

5

Kullanabilirsiniz Func bir parametre vardır ve TResult parametresi tarafından belirtilen türde bir değeri veren bir yöntem kapsüller.

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


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

4

Yalnızca Inside yöntemler değil, sınıflar içinde de kullanılabilir.

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);
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.