Programlama dilleri neden değişkenlerin ve fonksiyonların gölgelenmesine / gizlenmesine izin veriyor?


31

En popüler programlama dillerinin çoğu (C ++, Java, Python vb. Gibi) değişkenlerin veya fonksiyonların gizlenmesi / gölgelenmesi kavramına sahiptir . Gizleme ya da gölgeleme ile karşılaştığımda, hata bulmak zor olmuştu ve dillerin bu özelliklerini kullanmayı gerekli bulduğum bir durum görmedim.

Bana göre saklanmaya ve gölgelenmeye izin vermemek daha iyi olurdu.

Bu kavramların iyi bir şekilde kullanıldığını bilen var mı?

Güncelleme:
Sınıf üyelerinin (özel / korumalı üyeler) enkapsülasyonundan bahsetmiyorum.


Bu yüzden tüm alan isimlerim F ile başlar
Pieter B

7
Bence Eric Lippert’in bu konuda güzel bir makalesi var. Oh bekleyin, işte burada: blogs.msdn.com/b/ericlippert/archive/2008/05/21/…
Lescai Ionel

1
Lütfen sorunuzu netleştirin. Genel olarak gizlenen bilgiler hakkında mı yoksa türetilmiş bir sınıfın temel sınıfın işlevlerini gizlediği Lippert makalesinde açıklanan özel durum hakkında mı soruyorsunuz?
Aaron Kurtzhals

Önemli not: Gizleme / gölgelemenin yol açtığı hataların çoğu mutasyona neden olur (yanlış değişkeni ayarlamak ve değişimin neden "asla" olmadığını merak etmek). Öncelikle değişmez referanslarla çalışırken, gizleme / gölgeleme çok daha az soruna neden olur ve böceklere neden olma olasılığı daha düşüktür.
Jack,

Yanıtlar:


26

Saklanmaya ve gölgelenmeye izin vermezseniz, sahip olduğunuz şey tüm değişkenlerin global olduğu bir dildir.

Bu, küresel değişkenleri veya işlevleri gizleyebilecek yerel değişkenlere veya işlevlere izin vermekten açıkça daha kötü.

Eğer saklanarak ve gölgeleme izin vermemek, varsa VE sen çalışılan belirli küresel değişkenler, sen derleyici programcı "Üzgünüm Dave, ama bu adı kullanamazsınız söyleyen bir durum yaratacak "korumak", bu zaten kullanılıyor ." COBOL ile edinilen deneyim, programcıların hemen bu durumda küfürlere başvurduğunu göstermektedir.

Temel sorun gizleme / gölgeleme değil, küresel değişkenlerdir.


19
Yasaklı gölgelemenin başka bir dezavantajı, değişken bir yerel blokta kullanıldığından, global bir değişken eklemenin kodu kırabileceğidir.
Giorgio

19
"Gizleme ve gölgeye izin vermezseniz, sahip olduğunuz şey tüm değişkenlerin global olduğu bir dildir." - zorunlu değil: değişkenleri gölgelendirmeden kapsamış olabilirsiniz ve bunu açıkladınız.
Thiago Silva

@ThiagoSilva: Ve sonra Dil bu modül bu derleyici anlatmak için bir yol olması gerekiyor IS erişim o modülün "frammis" değişkeni izin verdi. Birisinin var bile tanımadığı bir nesneyi gizlemesine / gölgelemesine izin veriyor musunuz, yoksa neden bu ismi kullanmasına izin verilmediğini söylemesini mi söylüyorsunuz?
John R. Strohm

9
@Phil, sizinle aynı fikirde olmadığım için özür dilerim, ancak OP “değişkenlerin veya fonksiyonların gizlenmesi / gölgelenmesi” hakkında soru sordu ve “ebeveyn”, “çocuk”, “sınıf” ve “üye” kelimeleri sorusunda hiçbir yerde görünmedi. Bu isim kapsamı hakkında genel bir soru gibi görünüyor.
John R. Strohm

3
@dylnmc, bariz bir "2001: A Space Odyssey" referansı almayacak kadar genç bir whippersnapper ile karşılaşacak kadar uzun yaşamayı beklemiyordum.
John R. Strohm

15

Bu kavramların iyi bir şekilde kullanıldığını bilen var mı?

Tanımlayıcı, doğru tanımlayıcıları kullanmak her zaman iyi bir kullanımdır.

Değişken gizlemenin pek çok hataya neden olmadığını, çünkü aynı / benzer tipte iki benzer şekilde adlandırılmış değişkene sahip olmanın (değişken gizlemenin engellenmesine izin vermezseniz ne yapılacağını) çok sayıda böceğe ve / veya aynı ağır böcekler. Bu argümanın doğru olup olmadığını bilmiyorum , ama en azından makul bir şekilde tartışmalı.

Alanları ve yerel değişkenleri ayırt etmek için bir tür macar notasyonu kullanmak, bunun üstesinden gelir, ancak bakım (ve programlayıcı aklı başında) üzerinde kendi etkisi vardır.

Ve (belki de en büyük olasılıkla, kavramın ilk etapta bilinmesinin nedeni), dillerin gizleme / gölgeleme uygulaması için onu engellemekten çok daha kolaydır. Daha kolay uygulama, derleyicilerin hata yaşama olasılığının düşük olduğu anlamına gelir. Daha kolay uygulama, derleyicilerin daha az zaman harcadığı, daha erken ve daha geniş platformun benimsenmesine neden olduğu anlamına gelir.


3
Aslında, hayır, gizleme ve gölgelemeyi uygulamak kolay değildir. "Tüm değişkenler globaldir" uygulaması aslında daha kolaydır. Yalnızca bir ad alanına ihtiyacınız vardır ve birden fazla ad alanına sahip olmanın ve her bir adın dışa aktarılıp aktarılmayacağına karar vermenizin aksine, HER ZAMAN adı dışa aktarın.
John R. Strohm

5
@ JohnR.Strohm - Elbette, ancak herhangi bir kapsam belirleme yaptığınız anda (okuma: sınıflar), kapsamların daha düşük kapsamları gizlemesi ücretsiz olur.
Telastyn

Kapsam ve sınıflar farklı şeylerdir. BASIC hariç, programladığım her dilin kapsamı genişliyor, fakat hepsinde sınıf veya nesne kavramı yok.
Michael Shaw

@michaelshaw - tabii ki, daha net olmalıydım.
Telastyn

7

Sadece aynı sayfada olduğumuzdan emin olmak için, "gizleme" yöntemi türetilmiş bir sınıfın temel sınıftakiyle aynı adı taşıyan bir üyeyi tanımlamasıdır (ki eğer bir yöntem / özellikse sanal / geçersiz kılınabilir) ) ve "türetilmiş bağlamda" türetilmiş bir sınıfın bir örneğinden çağrıldığında, türetilmiş üye, aynı örnek tarafından temel sınıfın bağlamında çağrıldığında, temel sınıf üyesi kullanılır. Bu, temel sınıf üyesinin türetilmiş sınıfın bir değiştirme tanımlamasını beklediği yerde üye soyutlama / geçersiz kılmadan ve bir üyeyi istenen kapsam dışındaki tüketicilerden "gizleyen" kapsam / görünürlük değiştiricilerden farklıdır.

Niçin izin verildiğine dair kısa cevap, bunu yapmamanın, geliştiricileri nesne yönelimli tasarımın kilit şartlarını ihlal etmeye zorlayacağı yönündedir.

İşte daha uzun cevap; ilk olarak, C # 'nın üyelerin saklanmasına izin vermediği alternatif bir evrende şu sınıf yapısını düşünün:

public interface IFoo
{
   string MyFooString {get;}
   int FooMethod();
}

public class Foo:IFoo
{
   public string MyFooString {get{return "Foo";}}
   public int FooMethod() {//incredibly useful code here};
}

public class Bar:Foo
{
   //public new string MyFooString {get{return "Bar";}}
}

Üyeyi Bar’da rahatsız etmek istiyoruz ve böylelikle Bar’ın farklı bir MyFooString sunmasına izin veriyoruz. Ancak bunu yapamayız, çünkü üye gizlenmesinde alternatif gerçeklik yasağını ihlal eder. Bu özel örnek, böcekler için geçerli olacaktır ve neden yasaklamak isteyebileceğinizin bir örneğidir; örneğin, aşağıdakileri yaparsanız hangi konsol çıktısını alırsınız?

Bar myBar = new Bar();
Foo myFoo = myBar;
IFoo myIFoo = myFoo;

Console.WriteLine(myFoo.MyFooString);
Console.WriteLine(myBar.MyFooString);
Console.WriteLine(myIFoo.MyFooString);

Kafamın üstünden, son satırda "Foo" veya "Bar" alıp almayacağınızdan emin değilim. Her üç değişken de aynı durumda tam olarak aynı örneği referans almasına rağmen kesinlikle ilk satır için "Foo" ve ikincisi "Bar" alırsınız.

Dolayısıyla, dilin tasarımcıları, alternatif evrenimizde, mülkün gizlenmesini önleyerek bu açıkça kötü olan kodu reddetmektedir. Şimdi, kodlayıcı olarak tam olarak bunu yapmanız için gerçek bir gereksiniminiz var. Sınırlamayı nasıl aşarsınız? Eh, bir yolu Bar'ın mülkünü farklı şekilde adlandırmaktır:

public class Bar:Foo
{
   public string MyBarString {get{return "Bar";}}       
}

Tamamen yasal, ancak istediğimiz davranış değil. Bir Bar örneği, "Bar" üretmesini istediğimizde, MyFooString özelliği için her zaman "Foo" yu üretecektir. Yalnızca IFoo'muzun özellikle bir Bar olduğunu bilmek zorunda değiliz, aynı zamanda farklı erişimciyi kullanmayı da bilmek zorundayız.

Ayrıca, makul bir şekilde ebeveyn-çocuk ilişkisini unutabilir ve arayüzü doğrudan uygulayabiliriz:

public class Bar:IFoo
{
   public string MyFooString {get{return "Bar";}}
   public int FooMethod() {...}
}

Bu basit örnek için , Foo ve Bar'ın her ikisinin de IFoos olmasına özen gösterdiğiniz sürece mükemmel bir cevaptır . Bir çiftin örneklediği kullanım kodu, bir Bar Foo olmadığı ve bu şekilde atanamadığı için derleme başarısız olur. Bununla birlikte, Foo, Bar'ın ihtiyaç duyduğu faydalı bir "FooMethod" yöntemine sahipse, şimdi bu yöntemi miras alamazsınız; kodunu Bar'da klonlamanız veya yaratıcı olmanız gerekir:

public class Bar:IFoo
{
   public string MyFooString {get{return "Bar";}}
   private readonly theFoo = new Foo();

   public int FooMethod(){return theFoo.FooMethod();}
}

Bu bariz bir saldırıdır ve OO diline özgü bazı uygulamalar bundan daha azını tutarken, kavramsal olarak yanlış; Bar ihtiyacı tüketiciler Foo işlevselliğini ortaya çıkarmak için eğer, Bar gerektiğini olmak değil, bir Foo sahip bir Foo.

Açıkçası, eğer Foo'yu kontrol edersek, onu sanal yapabilir, sonra geçersiz kılabiliriz. Bu, bir üyenin geçersiz kılınması beklenen ve gizlenmeye izin vermeyen alternatif bir evrende tutacağı zaman, mevcut evrenimizdeki kavramsal en iyi uygulamadır:

public class Foo:IFoo
{
   public virtual string MyFooString {get{return "Foo";}}
   //...
}

public class Bar:Foo
{
   public override string MyFooString {get{return "Bar";}}
}

Bununla ilgili sorun, sanal üye erişiminin kaputun altında, gerçekleştirmek için nispeten daha pahalı olması ve bu nedenle genellikle yalnızca gerektiğinde yapmak istediğinizde. Ancak, saklanma eksikliği, sizi kaynak kodunuzu kontrol etmeyen başka bir kodlayıcının yeniden yerleştirmek isteyebileceği konusunda karamsar olmaya zorlar; mühürlü olmayan herhangi bir sınıf için "en iyi uygulama", özellikle istemediğiniz sürece her şeyi sanal yapmak olacaktır. Aynı zamanda yine size saklandığı yerden kesin davranışını vermez; Örnek bir Bar ise, dize her zaman "Bar" olur. Bazen, çalıştığınız kalıtım seviyesine bağlı olarak, gizli durum verisi katmanlarını kullanmak gerçekten yararlıdır.

Özetle, üyelerin saklanmasına izin vermek bu kötülüklerden daha azdır. Bunu yapmamak, genellikle nesne yönelimli ilkelere karşı işlenmiş zulümlere izin vermekten daha fazla yol açmaz.


Asıl soruyu ele almak için +1. Üye gizlenmesinin gerçek dünya kullanımına güzel bir örnek Eric Libbert'in konuyla ilgili blog yazısında açıklanan IEnumerableve IEnumerable<T>arayüzüdür .
Phil

Basmak saklanmak değildir . @Phil ile buna katılıyorum, bu soruyu ele alıyor.
Jan Hudec

Demek istediğim, geçersiz kılmanın gizlenme bir seçenek olmadığı zaman saklanmanın yerine geçeceği yönündeydi. Katılıyorum, saklanma değil ve ilk paragrafta çok fazla söylüyorum. Benim alternatif gerçeklik senaryosundaki geçici çözümlerden hiçbiri C # 'da saklanmamamı saklıyor; mesele bu.
KeithS

Gölgeleme / gizleme kullanımından hoşlanmıyorum. Gördüğüm başlıca iyi kullanımlar (1) bir temel sınıfın yeni sürümünün eski bir sürümün etrafında tasarlanan tüketici koduyla çelişen bir üyeyi içerdiği durumun etrafında dolaşmak [çirkin ama gerekli]; (2) geri dönüş tipi kovaryans gibi şeylere sahte olmak; (3) bir baz sınıf yönteminin belirli bir alt tipte çağrılabilir olduğu ancak kullanışlı olmadığı durumlarla ilgilenmek . LSP bir öncekini gerektirir, ancak temel sınıf sözleşmesi bazı yöntemlerin bazı durumlarda yararlı olmayabileceğini belirtirse ikincisini gerektirmez.
supercat

2

Dürüst olmak gerekirse, C # derleyici ekibinin ana geliştiricisi olan Eric Lippert bunu oldukça iyi açıklıyor (bağlantı için Lescai Ionel'e teşekkürler). .NET IEnumerableve IEnumerable<T>arayüzler, üye gizlenmenin faydalı olduğu durumlara iyi bir örnektir.

.NET'in ilk günlerinde, jeneriklerimiz yoktu. Böylece IEnumerablearayüz şöyle görünüyordu:

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}

Bu arayüz, foreachbir nesne koleksiyonunun üzerinden geçmemize izin verdi , ancak onları doğru kullanmak için bütün bu nesneleri kullanmamız gerekti.

Sonra jenerik geldi. Jenerikler aldığımızda yeni bir arayüz de var:

public interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

Şimdi onlar arasında yineleme yaparken nesneler dökmek zorunda değiliz! Woot! Şimdi, eğer üyelerin saklanmasına izin verilmediyse, arayüz bunun gibi bir şeye bakmak zorunda kalacaktı:

public interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> GetEnumeratorGeneric();
}

Bu biraz saçma olur, çünkü GetEnumerator()ve GetEnumeratorGeneric()her iki durumda da aynı şeyi yaparlar , ancak biraz farklı geri dönüş değerleri vardır. Aslında o kadar benzerler ki, jenerikler .NET ile tanıştırılmadan önce yazılmış eski kodlarla çalışmadıkça, hemen hemen her zaman genel biçimini varsayılan yapmak istersiniz GetEnumerator.

Bazen üye gizleme , kötü kodlar ve bulunması zor hatalar için daha fazla alan sağlar. Ancak bazen, eski kodu bozmadan bir dönüş türünü değiştirmek istediğinizde olduğu gibi kullanışlıdır. Bu, dil tasarımcılarının alması gereken kararlardan sadece biri: Bu özelliğe yasal olarak ihtiyaç duyan ve bunları dışarıda bırakan geliştiricilere rahatsız mı ediyoruz, yoksa bu özelliği dile ekleyip kötüye kullanımın mağduru olan kişilerin dilini alıyor muyuz?


Resmen IEnumerable<T>.GetEnumerator()gizler olsa da IEnumerable.GetEnumerator(), bunun nedeni sadece C # geçersiz kılma sırasında kovaryant dönüş tiplerine sahip olmamasıdır. Mantıksal olarak, tamamen LSP'ye uygun bir geçersiz kılmadır. Gizleme, mapdosyadaki işlevde yerel değişkeni bulunduğunda using namespace std(C ++ 'da).
Jan Hudec

2

Sorunuz iki yoldan okunabilir: genel olarak değişken / işlev kapsamı hakkında veya bir miras hiyerarşisinde kapsam hakkında daha spesifik bir soru soruyorsunuz. Özellikle mirastan bahsetmediniz, ama miras bağlamında düz kapsamdan ziyade kapsam gibi görünen hatalar bulmakta zorlandınız, bu yüzden her iki soruyu da cevaplayacağım.

Genel olarak kapsam iyi bir fikirdir, çünkü dikkatimizi programın belirli (umarım küçük) bir bölümüne odaklamamıza izin verir. Yerel adların her zaman kazanmasına izin verdiği için, programın yalnızca belirli bir kapsamda olan kısmını okursanız, o zaman hangi kısımların yerel olarak tanımlandığını ve başka bir yerde ne tanımlandığını bilirsiniz. Ad, yerel bir şeye atıfta bulunur; bu durumda onu tanımlayan kod tam önünüzdedir veya yerel kapsam dışında bir şeye referanstır. Altımızdan değişebilecek yerel olmayan referanslar yoksa (özellikle herhangi bir yerden değiştirilebilecek küresel değişkenler), programın yerel kapsamdaki kısmının referans göstermeden doğru olup olmadığını değerlendirebiliriz. Programın geri kalanının herhangi bir kısmına .

Muhtemelen, zaman zaman birkaç hataya yol açabilmektedir, ancak, muazzam miktarda başka olası hataları önleyerek daha fazla telafi etmektedir. Bir kütüphane işleviyle aynı adı taşıyan yerel bir tanım yapmaktan başka (bunu yapmayın), yerel kapsamdaki hataları tanıtmanın kolay bir yolunu göremiyorum, ancak yerel kapsam aynı programın birçok bölümünün kullanılmasına izin veren şeydir. i, birbirlerini gizlemeden bir döngü için indeks sayacı olarak ve Fred'in salonun aşağısında, str adında bir dizgeyi aynı isimle gizlemeyecek bir işlevi kullanan bir işlev yazmasını sağlar.

Bertrand Meyer tarafından kalıtsallık bağlamında aşırı yüklenmeyi tartışan ilginç bir yazı buldum . Sözdizimsel aşırı yükleme dediği şey (aynı adı taşıyan iki farklı şey olduğu anlamına gelir) ve anlamsal aşırı yükleme (aynı soyut fikrin iki farklı uygulaması olduğu anlamına gelir) arasında ilginç bir ayrım yaratır. Anlamsal aşırı yükleme iyi olurdu, çünkü bunu alt sınıfta farklı şekilde uygulamak istiyorsunuz; Sözdizimsel aşırı yükleme, bir hataya neden olan yanlışlıkla isim çarpışması olur.

Amaçlanan ve bir hata olan bir miras durumunda aşırı yükleme arasındaki fark anlambilimdir (anlam), bu nedenle derleyicinin ne yaptığınızın doğru veya yanlış olduğunu bilmenin bir yolu yoktur. Açık bir kapsam durumunda, doğru cevap her zaman yerel şeydir, bu nedenle derleyici doğru şeyin ne olduğunu çözebilir.

Bertrand Meyer'in önerisi, böyle bir isim çatışmasına izin vermeyen ve programcıyı bir veya her ikisini de yeniden adlandırmaya zorlayan, böylece sorunu tamamen ortadan kaldıran, Eyfel gibi bir dil kullanmak olacaktır. Benim önerim mirastan tamamen kaçınmak, aynı zamanda problemden tamamen kaçınmak. Bunlardan herhangi birini yapamıyor veya yapmak istemiyorsanız, mirasla ilgili bir problem yaşama olasılığını azaltmak için hala yapabileceğiniz şeyler var: LSP'yi takip edin (Liskov Değiştirme Prensibi), miras yerine kompozisyonu tercih edin, miras hiyerarşileriniz sığdır ve sınıfları bir miras hiyerarşisinde küçük tutar. Ayrıca, bazı diller, Eiffel gibi bir dilin yapacağı gibi, hata vermeseler bile bir uyarı verebilir.


2

İşte benim iki kuruş.

Programlar, bağımsız program mantığı birimleri olan bloklar (işlevler, prosedürler) halinde yapılandırılabilir. Her blok, isimleri / tanımlayıcıları kullanarak "şeylere" (değişkenler, fonksiyonlar, prosedürler) atıfta bulunabilir. İsimlerden eşyalara bu eşlemeye bağlayıcı denir .

Bir blok tarafından kullanılan isimler üç kategoriye ayrılır:

  1. Yerel olarak tanımlanmış isimler, örneğin sadece bloğun içinde bilinen yerel değişkenler.
  2. Blok çağrıldığında değerlere bağlı olan argümanlar ve arayan tarafından, bloğun giriş / çıkış parametresini belirtmek için kullanılabilir.
  3. Bloğun bulunduğu ortamda tanımlanmış ve bloğun kapsamı içinde olan dış adlar / bağlantılar.

Aşağıdaki C programını göz önünde bulundurun

#include<stdio.h>

void print_double_int(int n)
{
  int d = n * 2;

  printf("%d\n", d);
}

int main(int argc, char *argv[])
{
  print_double_int(4);
}

Fonksiyonun print_double_intbir yerel adı (yerel değişken) dve argümanı vardır nve printfkapsamda olan ancak yerel olarak tanımlanmayan harici, genel adı kullanır .

printfArgüman olarak da geçilebileceğine dikkat edin :

#include<stdio.h>

void print_double_int(int n, int printf(const char *, ...))
{
  int d = n * 2;

  printf("%d\n", d);
}

int main(int argc, char *argv[])
{
  print_double_int(4, printf);
}

Normalde, bir fonksiyonun giriş / çıkış parametrelerini belirtmek için bir argüman kullanılır (prosedür, blok), genel isimler "ortamda var" olan kütüphane fonksiyonları gibi şeylere atıfta bulunmak için kullanılır ve bu nedenle bunları belirtmek daha uygundur. sadece ihtiyaç duyulduğunda. Genel isimler yerine argümanları kullanmak , bağlama bakılmaksızın çözülmek yerine bağımlılıkların açık hale getirilmesi gerektiğinde kullanılan bağımlılık enjeksiyonunun ana fikridir .

Harici olarak tanımlanmış adların benzer bir başka kullanımı, kapaklarda bulunabilir. Bu durumda, bir bloğun sözcüksel bağlamında tanımlanan bir ad, blok içinde kullanılabilir ve bu isme bağlı değer (tipik olarak) blok kendisine atıfta bulunduğu sürece devam eder.

Örneğin bu Scala kodunu alın:

object ClosureExample
{
  def createMultiplier(n: Int) = (m: Int) => m * n

  def main(args: Array[String])
  {
    val multiplier3 = createMultiplier(3)
    val multiplier5 = createMultiplier(5)

    // Prints 6.
    println(multiplier3(2))

    // Prints 10.
    println(multiplier5(2))
  }
}

İşlevin dönüş değeri , argümanı ve harici adı içeren createMultiplierkapatmadır . Ad , kapatmanın tanımlandığı bağlama bakılarak çözülür: ad, işlev argümanına bağlanır . Bu bağlamanın, kapak oluşturulduğunda, yani çağrıldığında oluşturulduğuna dikkat edin . Bu nedenle, ad , işlevin belirli bir çağrısı için argümanın gerçek değerine bağlıdır. Buna , programın çalıştırılabilir yapısı oluşturulduğunda linker tarafından çözülen, gibi bir kütüphane işlevi söz konusu olduğunda kontrast oluşturur .(m: Int) => m * nmnnncreateMultipliercreateMultipliernprintf

Özetle, yerel bir kod bloğundaki harici adlara başvurmak faydalı olabilir;

  • harici olarak tanımlanmış isimleri açıkça argüman olarak belirtmek istemem / istemem ve
  • Bir blok oluşturulduğunda çalışma zamanında donmaları dondurabilir ve daha sonra blok çağrıldığında erişebilirsiniz.

Bir blokta yalnızca çevrede tanımlanan ilgili isimlerle, örneğin printfkullanmak istediğiniz işlevle ilgilendiğinizi düşündüğünüzde gölgeleme gelir . Şans eseri bir yerel adı kullanmak istiyorsanız ( getc, putc, scanf, ...) zaten ortamında kullanılan edildiğini, basit Talep küresel adını görmezden (gölge) için. Dolayısıyla, yerel olarak düşünürken, bütün (muhtemelen çok büyük) bağlamı göz önüne almak istemezsiniz.

Diğer taraftan, küresel düşünürken, yerel bağlamların içsel ayrıntılarını (kapsülleme) göz ardı etmek istersiniz. Bu nedenle gölgelemeye ihtiyacınız var, aksi takdirde genel bir ad eklemek, bu adı kullanan tüm yerel blokları kırabilir.

Alt satırda, harici olarak tanımlanmış bağlamalara atıfta bulunmak için bir kod bloğu istiyorsanız, yerel adları genel olanlardan korumak için gölgelemeye ihtiyacınız var.

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.