SORU
15 EKİM 2008, ÇARŞAMBA


Kullanarak "yeni" bir yapı üzerinde öbek üzerinde tahsis veya yığını mı?

new operatörü ile bir sınıf örneği oluşturduğunuzda, bellek öbek üzerinde tahsis. new operatörüyle bir yapı örneği oluşturduğunuzda bellek, yığın yığın veya ayrılmış mı ?

CEVAP
15 EKİM 2008, ÇARŞAMBA


Not: C# 6 sana özel yazmak yapılar için parametresiz yapıcı. Bu cevap daha önce yazılmıştır, ve bu değişime ayak uydurabilmek için revize etmedim. (Yapıcı, diziler açısından, jenerik vb çağrıldığında açısından karışıktır.)


Tamam, eğer bunu daha açık bir şekilde ifade edebilir miyim, bir bakayım.

Öncelikle, Ash haklı: sorudeğilhakkında nerede değer yazındeğişkenlerayrılan bellek. Farklı bir soru ve cevap değil sadece bir "yığın". Bu (ve daha da karmaşık C# 2) daha karmaşık. article on the topic bir ben var ve istenirse üzerine genişleyecektir, ama new operatörü ile baş başa bırak.

İkinci olarak, tüm bu gerçekten ne hakkında konuştuğunu bağlıdır. Derleyici şartlarda IL oluşturur kaynak kodu ile bakıyorum. JİT derleyici oldukça çok "" ayırma. mantıksal optimize uzakta açısından akıllıca şeyler yapmak mümkün.

Üçüncü olarak, çoğunlukla aslında kısmen şeyler çok karmaşık çünkü cevabı bilmiyorum çünkü jenerik görmezden geldim.

Son olarak, bütün bunlar geçerli uygulama ile. # Spec C bu pek belirtmeyen bir şekilde uygulanması bir ayrıntı. Yönetilen kod geliştiricilerin çok dikkat gerektiğini düşünenler var. O kadar uzağa gitmek istiyorum emin değilim, ama aslında tüm yerel değişkenler hala spec ile uyumlu olan yığınının üzerinde yaşadığı bir dünya hayal etmeye değer.

Değer türleri üzerinde new operatörü ile iki farklı durum vardır: ya parametresiz bir kurucu (*9 örneğin*) veya parameterful yapıcı (*10 örneğin*) diyebilirsiniz. Bu önemli ölçüde farklı IL oluşturmak. C karşılaştırmak gerek anlamak için# ve CLİ özellikleri: göre C#, tüm değer türleri parametresiz bir kurucu var. CLİ spec görehayırdeğer türleri parametresiz kurucular var. (Yansıma ile bir değer türü bir süre kurucular - bir parametresiz bir bulamazlar getir.)

C mantıklıdır# "sıfır ile bir değeri dilini tutar için" yapıcı, tutarlı - new(...) olarak düşünebilirsiniz . başlatma tedavisinde ^em>her zamanbir kurucu çağırmak. SİSTEMİNDE onu aramak için gerçek kod yok Hayır olarak farklı, ve kesinlikle yazın - özel kod düşünmek için mantıklı.

Ayrıca başlatıldı sonra değeri ile yapmak için gidiyoruz ne bir fark yapar. IL için kullanılır

Guid localVariable = new Guid(someString);

IL için farklı kullanılır:

myInstanceOrStaticVariable = new Guid(someString);

Eğer değer Ara değer olarak kullanılır ayrıca, eğer, örneğin, bir yöntem çağrısı için bir argüman, işler biraz farklı. Tüm bu farklılıkları göstermek için, burada kısa bir test programı. IL stfld stsfld arasındaki farkı olur ama hepsi bu. statik değişkenler ve örnek değişkenleri arasında fark görünmüyor:

using System;

public class Test
{
    static Guid field;

    static void Main() {}
    static void MethodTakingGuid(Guid guid) {}


    static void ParameterisedCtorAssignToField()
    {
        field = new Guid("");
    }

    static void ParameterisedCtorAssignToLocal()
    {
        Guid local = new Guid("");
        // Force the value to be used
        local.ToString();
    }

    static void ParameterisedCtorCallMethod()
    {
        MethodTakingGuid(new Guid(""));
    }

    static void ParameterlessCtorAssignToField()
    {
        field = new Guid();
    }

    static void ParameterlessCtorAssignToLocal()
    {
        Guid local = new Guid();
        // Force the value to be used
        local.ToString();
    }

    static void ParameterlessCtorCallMethod()
    {
        MethodTakingGuid(new Guid());
    }
}

İşte bu sınıf, alakasız bit hariç, IL (nops):

.class public auto ansi beforefieldinit Test extends [mscorlib]System.Object    
{
    // Removed Test's constructor, Main, and MethodTakingGuid.

    .method private hidebysig static void ParameterisedCtorAssignToField() cil managed
    {
        .maxstack 8
        L_0001: ldstr ""
        L_0006: newobj instance void [mscorlib]System.Guid::.ctor(string)
        L_000b: stsfld valuetype [mscorlib]System.Guid Test::field
        L_0010: ret     
    }

    .method private hidebysig static void ParameterisedCtorAssignToLocal() cil managed
    {
        .maxstack 2
        .locals init ([0] valuetype [mscorlib]System.Guid guid)    
        L_0001: ldloca.s guid    
        L_0003: ldstr ""    
        L_0008: call instance void [mscorlib]System.Guid::.ctor(string)    
        // Removed ToString() call
        L_001c: ret
    }

    .method private hidebysig static void ParameterisedCtorCallMethod() cil  managed    
    {   
        .maxstack 8
        L_0001: ldstr ""
        L_0006: newobj instance void [mscorlib]System.Guid::.ctor(string)
        L_000b: call void Test::MethodTakingGuid(valuetype [mscorlib]System.Guid)
        L_0011: ret     
    }

    .method private hidebysig static void ParameterlessCtorAssignToField() cil managed
    {
        .maxstack 8
        L_0001: ldsflda valuetype [mscorlib]System.Guid Test::field
        L_0006: initobj [mscorlib]System.Guid
        L_000c: ret 
    }

    .method private hidebysig static void ParameterlessCtorAssignToLocal() cil managed
    {
        .maxstack 1
        .locals init ([0] valuetype [mscorlib]System.Guid guid)
        L_0001: ldloca.s guid
        L_0003: initobj [mscorlib]System.Guid
        // Removed ToString() call
        L_0017: ret 
    }

    .method private hidebysig static void ParameterlessCtorCallMethod() cil managed
    {
        .maxstack 1
        .locals init ([0] valuetype [mscorlib]System.Guid guid)    
        L_0001: ldloca.s guid
        L_0003: initobj [mscorlib]System.Guid
        L_0009: ldloc.0 
        L_000a: call void Test::MethodTakingGuid(valuetype [mscorlib]System.Guid)
        L_0010: ret 
    }

    .field private static valuetype [mscorlib]System.Guid field
}

Gördüğünüz gibi, farklı talimatları kurucu çağırmak için kullanılan bir sürü vardır:

  • newobj: Bu değer, arama parametrik yapıcı yığını Ayırır. Ara değerler için, örneğin, bir alan için atama için kullanılan yöntem ya da bir değişken olarak kullanın.
  • call instance: Kullanır zaten ayrılmış bir depolama konumu yığını olsun ya da olmasın (). Bu yerel bir değişken atamak için yukarıdaki kod içinde kullanılır. Eğer aynı yerel değişken değeri birkaç kez new birkaç çağrılarını kullanarak atanırsa, sadece eski değeri - üstüne verilerini başlatıryokdaha fazla alan her zaman yığın ayırma.
  • initobj: zaten ayrılmış bir depolama konumu ve mendil sadece veri Kullanır. Bu tüm parametresiz yapıcı konuşmaları, yerel bir değişkene Atamak da dahil için kullanılır. Yöntem çağrısı için, Ara yerel bir değişken etkili bir şekilde tanıtıldı ve değeri initobj sildi.

Bu ne kadar karmaşık bir konu aynı zamanda ışık biraz parlak olsa, gösterir umarım.bazıkavramsal duyular, 23 ** Her çağrı yığını üzerinde yer ayırıyor ama gördüğümüz gibi, bu gerçekten de IL düzeyinde olacak şey değil. Özel bir durum belirtmek istiyorum. Bu yöntem:

void HowManyStackAllocations()
{
    Guid guid = new Guid();
    // [...] Use guid
    guid = new Guid(someBytes);
    // [...] Use guid
    guid = new Guid(someString);
    // [...] Use guid
}

Bu "mantıklı" 4 yığın ayırma için bir tane daha değişken ve her biri için üç new telefon ama aslında (açık kod) yığın yalnızca bir kez ayrılan ve daha sonra aynı depolama konumu yeniden.

EDİT: Sadece açık olmasını, bu tek gerçek bazı durumlarda... özellikle değeri guid olmayacak görünüyorsa Guid yapıcı bir istisna atar, bu yüzden C# derleyicisi dahil olmak üzere yeniden aynı yığın yuvası. Nereye Lippert blog post on value type construction Eric daha fazla bilgi ve bir dava için bkzyokuygulayın.

Bu cevabı yazmak çok - lütfen eğer herhangi belli değil açıklama isteyin öğrendim!

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • eisleyhead

    eisleyhead

    11 Ocak 2006
  • Truc Minh

    Truc Minh

    23 Ocak 2011
  • Vortez

    Vortez

    27 Temmuz 2009