C # satır içi işlevleri?


276

C # "satır içi işlevleri" nasıl yaparsınız? Kavramı anladığımı sanmıyorum. Anonim yöntemler gibi mi? Lambda fonksiyonları gibi mi?

Not : Cevaplar neredeyse tamamen fonksiyonların inline edilmesiyle ilgilidir , yani "bir fonksiyon çağrı sitesini callee gövdesiyle değiştiren bir manuel veya derleyici optimizasyonu". Anonim (aka lambda) işlevleriyle ilgileniyorsanız , @ jalf'ın cevabına bakın veya herkesin konuştuğu bu 'Lambda' nedir? .


11
Sonunda mümkün - cevabımı görün.
konrad.kruczynski

1
.NET 4.5 öncesi en yakın şey için Değişken lambda işlevi olarak nasıl tanımlanır sorusuna bakın . Aslında satır içi olarak derleme DEĞİLDİR, ancak satır içi bildirim gibi kısa bir işlev bildirimidir. Satır içi kullanarak ne yapmaya çalıştığınıza bağlıdır.
AppFzx

Meraklı kişiler için bu VS uzantısına göz atın .
TripleAccretion

Yanıtlar:


384

Son olarak .NET 4.5'te CLR, bir değeri kullanarak satır içi 1 yöntem önerebilir MethodImplOptions.AggressiveInlining. Aynı zamanda Mono'nun bagajında ​​da mevcuttur (bugün taahhüt edilmiştir).

// The full attribute usage is in mscorlib.dll,
// so should not need to include extra references
using System.Runtime.CompilerServices; 

...

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void MyMethod(...)

1 . Daha önce burada "kuvvet" kullanılmıştır. Birkaç downvotes olduğundan, terimi açıklığa kavuşturmaya çalışacağım. Yorumlarda ve dokümantasyonda olduğu gibi, The method should be inlined if possible.Özellikle Mono (açık olan) dikkate alındığında, satır içi veya daha genel olanı (sanal fonksiyonlar gibi) dikkate alan mono-spesifik teknik sınırlamalar vardır. Genel olarak, evet, bu derleyici bir ipucu, ama sanırım ne istedi.


17
+1 - Yanıtınızı çerçeve sürümü gereksinimleri hakkında daha spesifik olacak şekilde güncelleyin.
M.Babcock

4
Hala muhtemelen bir kuvvet değil , ama JITters buluşsal yöntemlerini geçersiz kılmak çoğu durumda kesinlikle yeterli.
CodesInChaos

7
Tüm .NET sürümleriyle çalışabilecek farklı bir yaklaşım, biraz fazla büyük bir yöntemi, diğerini çağıran, ikisi de 32 bayt IL'yi aşmayan iki yönteme bölmektir. Net etki orijinalin satır içi gibi.
Rick Sladkey

4
"Inlining" i zorlamak değil, sadece JIT ile konuşmaya çalışın ve programcının Inlining'i gerçekten burada kullanmak istediğini söyleyin, ancak JIT'in son sözü var. Bu nedenle MSDN: Mümkünse yöntem satır içine alınmalıdır.
Orel Eraki

11
Karşılaştırma olarak, C ++ 'ın satır içi önerileri, hatta derleyiciye özgü olanlar da aslında bir satır içi zorlamaz: tüm işlevler satır içine alınamaz (özyinelemeli fonksiyonlar gibi şeyler zordur, ancak başka durumlar da vardır). Yani evet, bu "tam olmayan" kuvvet tipiktir.
Eamon Nerbonne

87

Satır içi yöntemler, bir işlevin kodunun arayanın içine yuvarlandığı bir derleyici optimizasyonudur.

Bunu C # ile yapacak bir mekanizma yoktur ve desteklendikleri dillerde az miktarda kullanılmalıdırlar - neden bir yerde kullanılmaları gerektiğini bilmiyorsanız, olmamalıdırlar.

Düzenleme: Açıklığa kavuşturmak için, az miktarda kullanılması gereken iki önemli neden vardır:

  1. Gerekli olmadığı durumlarda satır içi kullanarak büyük ikili dosyalar oluşturmak kolaydır
  2. Derleyici, performans açısından bakıldığında bir şeyin çizgisel olması gerektiğinden daha iyi bilme eğilimindedir

Bir şeyleri yalnız bırakmak ve derleyicinin işini yapmasına izin vermek en iyisidir, ardından satır içi sizin için en iyi çözüm olup olmadığını profil ve hesaplayın. Tabii ki, bazı şeyler sadece çizgisel olmak için mantıklıdır (özellikle matematiksel operatörler), ancak derleyicinin bunu işlemesine izin vermek genellikle en iyi uygulamadır.


35
Normalde derleyicinin satır içi işlemeyi yapmasının sorun olmadığını düşünüyorum. Ancak derleyici kararını geçersiz kılmak ve satır içi veya satır içi yöntemi istemediğim durumlar var.
mmmmmmmm

7
@Joel Coehoorn: Modülerleşmeyi ve erişim korumasını bozacağı için bu kötü bir uygulama olurdu. (Özel üyelere erişen ve koddaki farklı noktalardan çağrılan bir sınıftaki satır içi yöntemi düşünün!)
mmmmmmmm

9
@Poma Satır içi kullanmak için garip bir nedendir. Etkili olacağından şüpheliyim.
Chris

56
Derleyicinin en iyisini bilmesi konusundaki tartışmalar yanlıştır. 32 IL bayttan daha uzun bir yöntem satır içi satmaz. Profiler tarafından tanımlanmış sıcak noktalara sahip projeler hakkında (benimki gibi Egor ve yukarıdaki gibi) kesinlikle hiçbir şey yapamayacağımız için korkunç. Hiçbir şey, yani, kodu kesip yapıştırmak ve böylece manuel olarak satır içi. Bu, bir kelimeyle, performansı gerçekten artırdığınızda korkunç bir durumdur.
parlak

6
Inlining göründüğünden daha incedir. Bellek, hızlı erişilebilen önbelleklere sahiptir ve kodun geçerli kısmı, tıpkı değişkenler gibi bu önbelleklerde saklanır. Bir sonraki önbellek talimat satırını yüklemek bir önbellek kaçırır ve tek bir talimatın yaptıklarından 10 veya daha fazla kez olabilir. Sonunda daha pahalı olan L2 önbelleğine yüklenmek zorunda. Bu nedenle, şişirilmiş kod daha fazla önbellek kaybına neden olabilir, burada aynı L1 önbellek satırlarında yerleşik olarak kalan satır içi işlev, daha az önbellek kaybına ve potansiyel olarak daha hızlı kodla sonuçlanabilir. Net ile daha da karmaşık.

56

Güncelleme: Başına konrad.kruczynski cevabı , şu .NET sürümleri kadar ve 4.0 dahil olmak için de geçerlidir.

Bir yöntemin satır içine alınmasını önlemek için MethodImplAttribute sınıfını kullanabilirsiniz ...

[MethodImpl(MethodImplOptions.NoInlining)]
void SomeMethod()
{
    // ...
}

... ama tam tersini yapmanın ve satır içi olmasını zorlamanın bir yolu yoktur .


2
Bunu bilmek ilginç, ama neden halt bir yöntemin inline edilmesini engelliyor? Monitöre bakarken biraz zaman geçirdim, ancak satır içi işlemin herhangi bir zarar vermesinin bir sebebini bulamıyorum.
Camilo Martin

3
Bir çağrı yığını (örn. NullReferenceException) alırsanız ve açıkça, yığının üstündeki yöntemin atması mümkün olmazsa. Tabii ki, çağrılarından biri olabilir. Fakat hangisi?
dzendras

19
GetExecutingAssemblyve GetCallingAssemblyyöntemin satır içi olup olmamasına bağlı olarak farklı sonuçlar verebilir. Bir yöntemi eğik olmaya zorlamak herhangi bir belirsizliği ortadan kaldırır.
Mart'ta

1
@Downvoter: Eğer yazdıklarıma katılmıyorsanız, nedenini açıklayan bir yorum göndermelisiniz. Sadece ortak bir nezaket değil, aynı zamanda toplam 20+ yanıtın oyu ile çok fazla başarı elde edemezsiniz.
BACON

2
Keşke +2 yapabilseydim çünkü senin adın tek başına +1: D hak ediyor.
retrodrone

33

İki ayrı kavramı karıştırıyorsunuz. İşlev satır içi, semantiğe etkisi olmayan bir derleyici optimizasyonudur. Bir işlev satır içi olsun ya da olmasın aynı şekilde davranır.

Öte yandan, lambda fonksiyonları tamamen semantik bir kavramdır. Dil spesifikasyonunda belirtilen davranışa uydukları sürece nasıl uygulanmaları veya yürütülmeleri gerektiği konusunda bir gereklilik yoktur. JIT derleyicisi böyle hissediyorsa satır içine alınabilir ya da değil.

C # 'da satır içi anahtar kelime yoktur, çünkü genellikle derleyiciye, özellikle de JIT' dillerinde bırakılabilen bir optimizasyondur. JIT derleyicisi, kod yazarken neye göre daha verimli satır içi olacağına karar vermesini sağlayan çalışma zamanı istatistiklerine erişebilir. Derleyici karar verirse bir işlev satır içine alınır ve bu konuda her iki şekilde de yapabileceğiniz hiçbir şey yoktur. :)


20
"Bir işlev satır içi olsun ya da olmasın aynı şekilde davranır." Bunun doğru olmadığı bazı nadir durumlar vardır: yani yığın izlemesi hakkında bilmek isteyen günlük işlevleri. Ama temel ifadenizi fazla zayıflatmak istemiyorum: bu genellikle doğrudur.
Joel Coehoorn

"ve bu konuda her iki şekilde de yapabileceğiniz hiçbir şey yok." - doğru değil. Derleyiciye hiçbir şey yapmadan "güçlü öneri" saymasak bile, işlevlerin satır içine alınmasını engelleyebilirsiniz.
BartoszKP

21

C ++ anlamında satır içi işlevler mi demek istediniz? Normal bir işlevin içeriği otomatik olarak satır yerine çağrı sitesine kopyalanır? Sonuç olarak, bir işlev çağrıldığında hiçbir işlev çağrısı gerçekleşmez.

Misal:

inline int Add(int left, int right) { return left + right; }

Eğer öyleyse hayır, buna C # eşdeğeri yoktur.

Veya başka bir işlev içinde bildirilen işlevler mi demek istediniz? Öyleyse evet, C # bunu anonim yöntemler veya lambda ifadeleri ile destekler.

Misal:

static void Example() {
  Func<int,int,int> add = (x,y) => x + y;
  var result = add(4,6);  // 10
}

21

Cody'nin hakkı var, ama satır içi fonksiyonun ne olduğuna bir örnek vermek istiyorum.

Diyelim ki bu koda sahipsiniz:

private void OutputItem(string x)
{
    Console.WriteLine(x);

    //maybe encapsulate additional logic to decide 
    // whether to also write the message to Trace or a log file
}

public IList<string> BuildListAndOutput(IEnumerable<string> x)
{  // let's pretend IEnumerable<T>.ToList() doesn't exist for the moment
    IList<string> result = new List<string>();

    foreach(string y in x)
    {
        result.Add(y);
        OutputItem(y);
    }
    return result;
}

Derleyici Just-In-Time iyileştirici artık şöyle bir kod yazmış gibi olurdu ki, yığının üzerine OutputItem () çağrısı yerleştirerek tekrar tekrar önlemek için kodu değiştirmek tercih edebilirsiniz:

public IList<string> BuildListAndOutput(IEnumerable<string> x)
{
    IList<string> result = new List<string>();

    foreach(string y in x)
    {
        result.Add(y);

        // full OutputItem() implementation is placed here
        Console.WriteLine(y);   
    }

    return result;
}

Bu durumda, OutputItem () işlevinin satıriçi olduğunu söyleyebiliriz. OutputItem () başka yerlerden de çağrılsa bile bunu yapabileceğini unutmayın.

Sıralı olma olasılığı daha yüksek bir senaryo gösterecek şekilde düzenlendi.


9
Sadece açıklığa kavuşturmak için; satırlamayı yapan JIT'tir; C # derleyicisi değil.
Marc Gravell

Ayrıca, en azından JIT'in montaj sınırları boyunca bile statik yöntemleri 'tercih' edeceğini unutmayın. Bu nedenle, optimizasyon için eski bir okul tekniği yöntemlerinizi statik olarak işaretlemekti. Bu, o zamandan beri topluluk tarafından kaşlarını çattı, ancak bu güne kadar iç, polimorfik olmayan ve tipik olarak yürütmek için ilişkili yığın dolgusu + dökülmesinden daha düşük maliyetli satır içi yöntemleri tercih edeceğim.
Shaun Wilson

7

Evet Kesinlikle tek fark, bir değer döndürmesidir.

Sadeleştirme (ifadeleri kullanmamak):

List<T>.ForEach Bir işlem yapar, bir dönüş sonucu beklemez.

Yani bir Action<T>delege yeterli olur ..

List<T>.ForEach(param => Console.WriteLine(param));

demekle aynıdır:

List<T>.ForEach(delegate(T param) { Console.WriteLine(param); });

aradaki fark, param tipi ve delege deklerasyonunun kullanımla çıkarılması ve parantezlerin basit bir satır içi yöntemde gerekli olmamasıdır.

Buna karşılık

List<T>.Where Sonuç bekleyen bir işlev alır.

Yani bir Function<T, bool>beklenen:

List<T>.Where(param => param.Value == SomeExpectedComparison);

ki aynı:

List<T>.Where(delegate(T param) { return param.Value == SomeExpectedComparison; });

Ayrıca bu yöntemleri satır içinde bildirebilir ve IE değişkenlerine atayabilirsiniz:

Action myAction = () => Console.WriteLine("I'm doing something Nifty!");

myAction();

veya

Function<object, string> myFunction = theObject => theObject.ToString();

string myString = myFunction(someObject);

Umarım bu yardımcı olur.


2

Kodun sıralı olmasını zorlamak istediğim durumlar var.

Örneğin, oldukça yinelemeli bir blok içinde çok sayıda kararın verildiği ve bu kararların benzer ancak biraz farklı eylemlerin gerçekleştirilmesine neden olan karmaşık bir rutinim varsa. Örneğin, sıralama algoritmasının, öğeleri hızlı bir dil için gramatik ve anlamsal ölçütlere göre sıralıyorlarsa yapabileceği gibi bir dizi farklı ilişkisiz kritere göre sıraladığı karmaşık (DB tabanlı olmayan) bir sıralama karşılaştırıcısını düşünün tanıma sistemi. Kaynak kodun okunabilirliğini ve modülerliğini korumak için bu eylemleri gerçekleştirmek için yardımcı işlevler yazma eğilimindeyim.

Bu yardımcı fonksiyonların sıralı olması gerektiğini biliyorum, çünkü kod asla bir insan tarafından anlaşılmasaydı yazılacaktı. Kesinlikle bu durumda ek yükü çağıran bir fonksiyon olmadığından emin olmak isterim.


2

"Bunları yalnız bırakmak ve derleyicinin işi yapmasına izin vermek en iyisi" ifadesi (Cody Brocious) tam anlamıyla rubish. 20 yıldır yüksek performanslı oyun kodunu programlıyorum ve henüz hangi kodun satır içi (fonksiyonlar) olması gerektiğini bilmek için 'yeterince akıllı' bir derleyiciye rastlamadım. Bu c # bir "satır içi" ifadesi olması yararlı olacaktır, gerçek şu ki derleyici sadece "satır içi" ipucu olmadan hangi işlev her zaman satır içi ya da değil olması gerektiğini belirlemek için gereken tüm bilgilere sahip değildir. Fonksiyon küçük (erişimci) olduğundan emin olun, otomatik olarak satır içine alınabilir, ancak birkaç satırlık kod varsa? Nonesense, derleyici bilmenin hiçbir yolu yoktur, sadece optimize edilmiş kod (algoritmaların ötesinde) için derleyiciye bırakamazsınız.


3
"Nonesense, derleyici bilmenin hiçbir yolu yok" JITer gerçek zamanlı fonksiyon çağrıları profilleme yapıyor ..
Brian Gordon

3
.NET JIT'in derleme zamanında 2 geçişli statik analiz ile çalışma zamanında mümkün olandan daha iyi optimize ettiği bilinmektedir. "Eski okul" kodlayıcılarının kavraması zor olan nokta, derleyici değil JIT'in yerel kod üretiminden sorumlu olmasıdır. Derleyici değil JIT, satır içi yöntemlerden sorumludur.
Shaun Wilson

1
Aradığım kodu kaynak kodum olmadan derlemem mümkün ve JIT çağrıyı satır içine almayı seçebilir. Normalde aynı fikirdeyim, ama bir insan olarak artık aletleri geçemeyeceğinizi söyleyebilirim.
Shaun Wilson


0

Hayır, C # 'da böyle bir yapı yoktur, ancak .NET JIT derleyicisi JIT zamanında satır içi işlev çağrıları yapmaya karar verebilir. Ama aslında gerçekten böyle optimizasyonlar yapıp yapmadığını bilmiyorum.
(Sanırım :-))


0

Derlemelerinizin ngen-edileceği durumda, TargetedPatchingOptOut'a bakmak isteyebilirsiniz. Bu, ngen'in satır içi yöntemleri belirleyip belirlemeyeceğine karar verecektir. MSDN başvurusu

Yine de, zorunlu kılan bir komut değil, optimize etmek sadece beyan edici bir ipucudur.


Ve JIT, inline olsun ya da olmasın çok daha iyi bilecektir. Açıkçası, JIT çağrı sitesini optimize etmezse size yardımcı olmaz, ancak neden sadece birkaç kez çağrılan bir fonksiyonun minimum yükü hakkında endişelenmeliyim?
Voo

-7

Lambda ifadeleri satır içi işlevlerdir! Bence, C # inline veya bunun gibi bir ekstra özelliğe sahip değil!


9
Sizi küçümsemedim, ancak rastgele bir cevap göndermeden önce lütfen soruları okuyun ve anladığınızdan emin olun. en.wikipedia.org/wiki/Inline_function
Camilo Martin

1
Bu cevap, ortaya konulduğu kadar kötü değil - OP, 'lambda fonksiyonlarına' karşı 'işlev satır içi' hakkında net değil ve maalesef MSDN, lambdaslara "satır içi ifadeler" veya "satır içi kod" olarak atıfta bulunuyor . Ancak, Konrad'ın Yanıt başına gibi bir orada nitelik bir yöntem inlining hakkında derleyiciye ipucu için.
StuartLC

-7

C #, python gibi dinamik dillerin yaptığı gibi satır içi yöntemleri (veya işlevleri) desteklemez. Bununla birlikte, anonim yöntemler ve lambdalar, aşağıdaki örnekte olduğu gibi, içeren yöntemde bir değişkene ne zaman erişmeniz gerektiği de dahil olmak üzere benzer amaçlar için kullanılabilir.

static void Main(string[] args)
{
    int a = 1;

    Action inline = () => a++;
    inline();
    //here a = 2
}

10
Bunun satır içi işlevlerle ne ilgisi var? Bu Anonim yöntemdir.
Tomer W
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.