Geçersiz olmayan bir yöntemde eksik dönüş ifadesi derlenir


189

Geçersiz olmayan bir yöntem bir return deyimi eksik ve kod hala derler bir durumla karşılaştım . While döngüsü sonrası ifadelere ulaşılamaz (ölü kod) ve asla yürütülmeyeceğini biliyorum. Peki derleyici neden bir şey döndürmek konusunda uyarmıyor? Veya bir dil neden sonsuz bir döngüye sahip olan ve hiçbir şey döndürmeyen geçersiz bir yönteme sahip olmamıza izin verir?

public int doNotReturnAnything() {
    while(true) {
        //do something
    }
    //no return statement
}

While döngüsünde bir break deyimi (hatta bir koşullu) eklersem, derleyici rezil hatalardan şikayet eder: Method does not return a valueEclipse ve Not all code paths return a valueVisual Studio'da.

public int doNotReturnAnything() {
    while(true) {
        if(mustReturn) break;
        //do something
    }
    //no return statement
}

Bu hem Java hem de C # için geçerlidir.


3
Güzel soru. Bunun sebebi ile ilgilenirim.
Erik Schierboom

18
Bir tahmin: sonsuz bir döngüdür, bu yüzden bir dönüş kontrol akışı önemsizdir?
Lews Therin

5
"Bir dil neden sonsuz bir döngüye sahip olan ve hiçbir şey döndürmeyen geçersiz bir yönteme sahip olmamıza izin verir?" <- görünüşte aptalca olsa da, soru da tersine çevrilebilir: buna neden izin verilmiyordu? Bu gerçek kod bu arada mı?
fge

3
Java için bu cevapta güzel bir açıklama bulabilirsiniz .
Keppil

4
Diğerlerinin açıkladığı gibi, derleyici döngünün sonsuz olduğunu bilecek kadar akıllı olmasıdır. Derleyicinin yalnızca dönüşün eksik olmasına izin vermekle kalmayıp , döngüye erişilemediğinden sonra bir şey bildiğinden bunu uyguladığını unutmayın . En azından Netbeans'de, döngüden sonra bir şeyunreachable statement olup olmadığı tam anlamıyla şikayet edecek .
Supr

Yanıtlar:


240

Bir dil neden sonsuz bir döngüye sahip olan ve hiçbir şey döndürmeyen geçersiz bir yönteme sahip olmamıza izin verir?

Geçersiz olmayan yöntemlerin kuralı , döndürülen her kod yolunun bir değer döndürmesi gerektiğidir ve bu kural programınızda karşılanır: dönen sıfır kod yolundan sıfır, bir değer döndürür. Kural "geçersiz olmayan her yöntemin dönen bir kod yolu olması gerekir" değildir.

Bu, aşağıdaki gibi saplama yöntemleri yazmanıza olanak tanır:

IEnumerator IEnumerable.GetEnumerator() 
{ 
    throw new NotImplementedException(); 
}

Bu geçersiz bir yöntem. Bu sahip arayüz karşılamak için olmayan bir boşluk bir yöntem olduğu. Ancak hiçbir şey döndürmediği için bu uygulamayı yasadışı yapmak aptalca görünüyor.

Yöntemin, (başka bir biçimi olan ) yerine bir goto(hatırla, a while(true)yazmak için sadece daha hoş bir yoldur goto) nedeniyle ulaşılamaz bir bitiş noktasına sahip olması önemli değildir.throwgoto

Derleyici neden bir şey döndürmek konusunda uyarmıyor?

Çünkü derleyicinin kodun yanlış olduğuna dair iyi bir kanıtı yoktur. Birisi yazdı while(true)ve bunu yapan kişinin ne yaptığını bilmesi muhtemel görünüyor.

C # 'da erişilebilirlik analizi hakkında daha fazla bilgiyi nerede bulabilirim?

Konu ile ilgili makalelerimi burada görebilirsiniz:

ATBG: de facto ve de jure ulaşılabilirliği

Ayrıca C # belirtimini de okumayı düşünebilirsiniz.


Peki neden bu kod derleme zamanı hatası veriyor: public int doNotReturnAnything() { boolean flag = true; while (flag) { //Do something } //no return } Bu kod da bir kırılma noktası yok. Şimdi derleyici kodun yanlış olduğunu nasıl biliyor.
Sandeep Poonia

@SandeepPoonia: Buradaki diğer cevapları okumak isteyebilirsiniz ... Derleyici yalnızca belirli koşulları algılayabilir.
Daniel Hilgarth

9
@SandeepPoonia: boolean bayrağı çalışma zamanında değiştirilebilir, çünkü bir sabit değildir, bu nedenle döngü mutlaka sonsuz değildir.
Schmuli

9
@SandeepPoonia: C # özellikleri söylüyor if, while, for, switch, vs, ameliyat dallanma yapılar sabitleri derleyici tarafından koşulsuz dallar olarak kabul edilir. Sabit ifadenin kesin tanımı spesifikasyondadır. Sorularınızın cevapları spesifikasyondadır; tavsiyem onu ​​okumanız.
Eric Lippert

1
Every code path that returns must return a valueEn iyi cevap. Her iki soruyu da açıkça ele alıyor. Teşekkürler
cPu1

38

Java derleyicisi, erişilemeyen kodu ( whiledöngüden sonraki kod ) bulabilecek kadar akıllı

ve onun beri ulaşılamaz , hiçbir nokta yoktur bir ekleme de returnorada deyimi (sonra whileuçları)

aynı şartlı if

public int get() {
   if(someBoolean) {   
     return 10;
   }
   else {
     return 5;
   }
   // there is no need of say, return 11 here;
}

boolean koşul beri someBooleanya sadece değerlendirebilir trueya false, orada bir sağlamak gerek yoktur return açıkça sonra if-elsebu kod olduğu için, ulaşılamaz ve Java bu konuda şikayet etmez.


4
Bunun soruyu gerçekten ele aldığını sanmıyorum. Bu return, erişilemeyen kodda bir ifadeye neden ihtiyacınız olmadığını , ancak OP'nin kodunda neden herhangi bir return ifadeye ihtiyacınız olmadığını ilgilendirmez.
Bobson

Java derleyicisi erişilemeyen kodu (while döngüsü sonrası kod) bulabilecek kadar akıllıysa, aşağıdaki kodların neden derlendiğini, her ikisinde de erişilemeyen kodu var ancak if yöntemiyle bir return ifadesi gerektirir, ancak bir süre içeren yöntem içermez. public int doNotReturnAnything() { if(true){ System.exit(1); } return 11; } public int doNotReturnAnything() { while(true){ System.exit(1); } return 11;// Compiler error: unreachable code }
Sandeep Poonia

@SandeepPoonia: Çünkü derleyici System.exit(1)bunun programı öldüreceğini bilmiyor . Yalnızca belirli erişilemeyen kod türlerini algılayabilir.
Daniel Hilgarth

@Daniel Hilgarth: Tamam derleyici System.exit(1)programı öldüreceğini bilmiyor , herhangi birini kullanabiliriz return statement, şimdi derleyici tanıyor return statements. Ve davranış aynı, dönüş için gerektirir if conditionama değil while.
Sandeep Poonia

1
Ulaşılamayan bölümün özel bir durum kullanmadan iyi bir şekilde gösterilmesiwhile(true)
Matthew

17

Derleyici, whiledöngünün yürütmeyi asla durduramayacağını, dolayısıyla yöntemin asla bitmeyeceğini bilir , dolayısıyla bir returnifade gerekli değildir.


13

Döngünüzün bir sabit üzerinde yürütüldüğü göz önüne alındığında, derleyici bunun sonsuz bir döngü olduğunu bilir - yani yöntem zaten geri dönemez.

Bir değişken kullanırsanız - derleyici kuralı uygular:

Bu derlenmeyecektir:

// Define other methods and classes here
public int doNotReturnAnything() {
    var x = true;

    while(x == true) {
        //do something
    }
    //no return statement - won't compile
}

Peki ya "bir şey yap" x'i hiçbir şekilde değiştirmezse .. derleyici bunu anlamak o kadar akıllı değil mi? Serseri :(
Lews Therin

Hayır - öyle görünmüyor.
Dave Bish

@ Dönen int olarak işaretlenmiş bir yönteminiz varsa, ancak aslında geri dönmüyorsa, derleyicinin bunu işaretlemesinden memnun olmalısınız, böylece yöntemi geçersiz olarak işaretleyebilir veya bunu yapmadıysanız düzeltebilirsiniz. davranışı.
MikeFHay

@MikeFHay Evet, bunu tartışmamak.
Lews Therin

1
Yanılıyor olabilirim ama bazı hata ayıklayıcıları değişkenlerin değiştirilmesine izin veriyor. Burada x kodu tarafından değiştirilmez ve JIT tarafından optimize edilirken, biri x'i false olarak değiştirebilir ve yöntem bir şey döndürmelidir (eğer böyle bir şeye C # debugger tarafından izin verilirse).
Maciej Piechotka

11

Java belirtimi adlı bir kavramı tanımlar Unreachable statements. Kodunuzda erişilemez bir ifadeye sahip olmanıza izin verilmez (derleme zamanı hatasıdır). Bir süre sonra iade ifadesine sahip olmanıza bile izin verilmez (true); Java deyimi. Bir while(true);ifade aşağıdaki ifadelere tanım gereği erişilemez olmasını sağlar, bu nedenle bir returnifadeye ihtiyacınız yoktur .

İken Not olduğunu Halting problemi jenerik durumda undecidable, Ulaşılamıyor Bildirimi tanımı sadece durdurulması daha sıkı. Bir programın kesinlikle durmadığı çok özel durumlara karar verir. Derleyici teorik olarak tüm sonsuz döngüleri ve erişilemez ifadeleri algılayamaz, ancak spesifikasyonda tanımlanan belirli vakaları tespit etmelidir (örneğin, while(true)vaka)


7

Derleyici, döngünüzün whilesonsuz olduğunu anlayacak kadar akıllıdır .

Yani derleyici sizin için düşünemez. Bu kodu neden yazdığınızı tahmin edemiyor . Aynı yöntem yöntemlerin dönüş değerleri için de geçerlidir. Java, yöntemin dönüş değerleriyle hiçbir şey yapmazsanız şikayet etmeyecektir.

Sorunuzu cevaplamak için:

Derleyici kodunuzu analiz eder ve hiçbir yürütme yolunun, OK ile biten işlevin sonundan düşmesine yol açmadığını öğrendikten sonra.

Sonsuz bir döngünün meşru nedenleri olabilir. Örneğin, birçok uygulama sonsuz bir ana döngü kullanır. Başka bir örnek, istekleri süresiz olarak bekleyebilen bir web sunucusudur.


7

Tip teorisinde, alt tip olarak adlandırılan bir şey var diğer her türün (!) Bir alt sınıfı olan ve diğer şeyler arasında sonlandırılmayı belirtmek için kullanılan bir şey vardır. (İstisnalar bir tür feshetme olarak sayılmayabilir - normal yoldan sonlandırmazsınız.)

Teorik bir bakış açısından, sonlandırılmamış olan bu ifadelerin int türünün bir alt türü olan Alt türden bir şey döndürdüğü düşünülebilir, bu nedenle sonuç değerinizi bir tür perspektifinden alırsınız. Ve bir türün int de dahil olmak üzere her şeyin bir alt sınıfı olabileceğine dair hiçbir anlam ifade etmemesi gayet iyidir, çünkü aslında bir daha asla dönmezsiniz.

Her durumda, açık tür teorisi yoluyla ya da değil, derleyiciler (derleyici yazarlar), sonlandırılmamış bir ifadeden sonra bir dönüş değeri istemenin saçma olduğunu kabul eder: bu değere ihtiyaç duyabileceğiniz olası bir durum yoktur. (Derleyicinizin bir şeyin sona ermeyeceğini bildiği zaman sizi uyarması güzel olabilir, ancak bir şey geri vermesini istediğiniz gibi görünüyor. Ama bu, stil denetleyicileri için la bir tiftik için daha iyi kalıyor, çünkü belki de tür imzasına ihtiyacınız var başka bir nedenden ötürü (örneğin, alt sınıflama) ama gerçekten feshetmek istemiyorsunuz.)


6

Uygun bir değer döndürmeden işlevin sonuna ulaşabileceği bir durum yoktur. Bu nedenle, derleyicinin şikayet edeceği bir şey yoktur.


5

Visual studio, bir dönüş türü yazıp yazmadığınızı algılamak için akıllı motora sahiptir.

PHP'de olduğu gibi, herhangi bir şey döndürmediyseniz, iade türünüz doğrudur. hiçbir şey dönmediyse derleyici 1 olsun.

Bu nedenle

public int doNotReturnAnything() {
    while(true) {
        //do something
    }
    //no return statement
}

Derleyici, ifadenin kendisinin onu dikkate almamak için sonsuz bir doğası olduğunu bilir. php derleyici ise while ifadesinde bir koşul yazarsanız otomatik olarak gerçekleşir.

Ancak VS durumunda değil, yığınta bir hata döndürür.


4

While döngünüz sonsuza kadar çalışır ve bu nedenle dışarıya çıkmaz; yürütmeye devam edecektir. Bu nedenle, while {} öğesinin dış kısmına erişilemez ve yazılı olarak iade veya dönüş noktası yoktur. Derleyici, hangi parçanın ulaşılabilir olduğunu ve hangi parçanın bulunmadığını anlayacak kadar akıllıdır.

Misal:

public int xyz(){
    boolean x=true;

    while(x==true){
        // do something  
    }

    // no return statement
}

Yukarıdaki kod derlenmeyecektir, çünkü x değişkeni değerinin while döngüsü gövdesi içinde değiştirildiği bir durum olabilir. Bu, while döngüsünün dış kısmını erişilebilir hale getirir! Ve böylece derleyici 'dönüş ifadesi bulunamadı' hatası verir.

Derleyici, x değerinin değiştirilip değiştirilmeyeceğini anlamaya yetecek kadar zeki (ya da daha çok tembel;)) değildir. Umarım bu her şeyi temizler.


7
Aslında bu durumda derleyicinin yeterince akıllı olması söz konusu değil. C # 2.0'da derleyici, int x = 1; while(x * 0 == 0) { ... }sonsuz bir döngü olduğunu bilecek kadar akıllıydı , ancak belirtim , derleyicinin yalnızca döngü ifadesi sabit olduğunda kontrol akışı kesintileri yapması gerektiğini ve belirtimin sabit bir ifadeyi değişken içermediğini tanımladığını söylüyor . Derleyici bu nedenle çok akıllıydı . C # 3'te derleyiciyi şartname ile eşleştirdim ve o zamandan beri bu tür ifadeler hakkında kasıtlı olarak daha az akıllı.
Eric Lippert

4

"Derleyici neden bir şey döndürmek konusunda uyarmıyor? Ya da bir dil neden sonsuz bir döngüye sahip olan ve hiçbir şey döndürmeyen geçersiz bir yönteme sahip olmamıza izin verir?".

Bu kod diğer tüm dillerde de geçerlidir (muhtemelen Haskell hariç!). Çünkü ilk varsayım "kasıtlı olarak" bazı kodlar yazmamızdır.

Ve bu kodun bir iş parçacığı olarak kullanacaksınız gibi tamamen geçerli olabileceği durumlar vardır; ya Task<int>da döndürülüyorsa, döndürülmemesi gereken döndürülen int değerine dayalı olarak bazı hata denetimleri yapabilirsiniz.


3

Yanılıyor olabilirim ama bazı hata ayıklayıcıları değişkenlerin değiştirilmesine izin veriyor. Burada x kodu tarafından değiştirilmez ve JIT tarafından optimize edilirken, biri x'i false olarak değiştirebilir ve yöntem bir şey döndürmelidir (eğer böyle bir şey C # debugger tarafından izin verilirse).


1

Bunun için Java durumunun özellikleri (muhtemelen C # durumuna çok benzer), Java derleyicisinin bir yöntemin geri dönüp dönmediğini nasıl belirlediğiyle ilgilidir.

Özellikle, kurallar, dönüş türüne sahip bir yöntemin normal olarak tamamlanmaması ve bunun yerine her zaman JLS 8.4.7 uyarınca aniden (aniden burada bir iade ifadesi veya bir istisna ile gösterilir) tamamlanması gerektiğidir .

Bir yöntemin bir dönüş türüne sahip olduğu bildirilirse, yöntemin gövdesi normal olarak tamamlanabilirse, derleme zamanı hatası oluşur. Başka bir deyişle, döndürme türüne sahip bir yöntem yalnızca değer döndüren bir döndürme deyimi kullanarak dönmelidir; "vücudunun ucunu düşürmesine" izin verilmez .

Derleyici , JLS 14.21 Ulaşılamaz Beyanlarında tanımlanan kurallara dayanarak normal sonlandırmanın mümkün olup olmadığını görmektedir. ve normal tamamlama kurallarını da tanımladığını araştırmaktadır.

Özellikle, ulaşılamayan ifadelerin kuralları, yalnızca truesabit bir ifadesi tanımlanmış döngüler için özel bir durum oluşturur :

Bir while ifadesi, aşağıdakilerden en az biri doğru olduğunda normal şekilde tamamlanabilir:

  • While ifadesine ulaşılabilir ve koşul ifadesi, true değerine sahip sabit bir ifade (§15.28) değildir.

  • While ifadesinden çıkan ulaşılabilir bir break ifadesi vardır.

Eğer whileifade normal şekilde tamamlanabiliyorsa , o zaman kodun erişilebilir olduğu kabul edildiğinden ve geri whileulaşılabilir bir kesme ifadesi veya sabit trueifadesi olmayan herhangi bir döngü normal olarak tamamlanabileceği kabul edildiğinden, aşağıdaki bir dönüş ifadesi gereklidir .

Bu kurallar sizin anlamına whilesabit gerçek ifadesi ile ve bir olmadan ifadesi breakolduğunu normalde tamamlamak için düşünülen asla ve onun altındaki tüm kod bu yüzden hiç düşünmedi ulaşılabilir olması . Yöntemin sonu döngünün altındadır ve döngünün altındaki her şeye ulaşılamadığından, yöntemin sonu da böyledir ve bu nedenle yöntem muhtemelen normal olarak tamamlanamaz (bu, karşılaştırıcının aradığı şeydir).

if Diğer yandan ifadeler, döngülere sağlanan sabit ifadelerle ilgili özel muafiyete sahip değildir.

Karşılaştırmak:

// I have a compiler error!
public boolean testReturn()
{
    final boolean condition = true;

    if (condition) return true;
}

İle:

// I compile just fine!
public boolean testReturn()
{
    final boolean condition = true;

    while (condition)
    {
        return true;
    }
}

Ayrımın nedeni oldukça ilginçtir ve derleyici hatalarına neden olmayan koşullu derleme bayraklarına izin verme arzusundan kaynaklanmaktadır (JLS'den):

Bir if ifadesinin aşağıdaki şekilde ele alınması beklenebilir:

  • Aşağıdakilerden en az biri doğruysa if-then ifadesi normal olarak tamamlanabilir:

    • İf-then ifadesine ulaşılabilir ve koşul ifadesi, değeri doğru olan sabit bir ifade değildir.

    • Then-ifadesi normal şekilde tamamlanabilir.

    İf-then deyimine erişilebildiğinde ve koşul ifadesi değeri false olan sabit bir ifade olmadığında then-ifadesine ulaşılabilir.

  • İf-then-else deyimi normalde tamamlanırsa, then-ifadesi normal şekilde tamamlanabilirse veya else-ifadesi normal olarak tamamlanabilirse.

    • İf-then-else deyimine erişilebildiğinde ve koşul ifadesi değeri false olan sabit bir ifade olmadığında then-ifadesine ulaşılabilir.

    • İf-then-else deyimine erişilebiliyorsa ve koşul ifadesi değeri doğru olan sabit bir ifade değilse else-ifadesine ulaşılabilir.

Bu yaklaşım, diğer kontrol yapılarının tedavisi ile tutarlı olacaktır. Ancak, if ifadesinin "koşullu derleme" amaçları için rahatça kullanılmasına izin vermek için, gerçek kurallar farklıdır.

Örnek olarak, aşağıdaki ifade derleme zamanı hatasına neden olur:

while (false) { x=3; }çünkü ifadeye x=3;ulaşılamaz; fakat yüzeysel olarak benzer durum:

if (false) { x=3; }derleme zamanı hatasına neden olmaz. Optimize edici bir derleyici, ifadenin x=3;hiçbir zaman yürütülmeyeceğini anlayabilir ve bu ifadenin kodunu oluşturulan sınıf dosyasından çıkarmayı seçebilir, ancak ifadex=3; burada belirtilen teknik anlamda "ulaşılamaz" olarak kabul edilmez.

Bu farklı tedavinin mantığı, programcıların aşağıdaki gibi "bayrak değişkenleri" tanımlamasına izin vermektir:

static final boolean DEBUG = false; ve sonra aşağıdaki gibi bir kod yazın:

if (DEBUG) { x=3; } Fikir, DEBUG değerini false yerine true veya true yerine false olarak değiştirmek ve sonra program metninde başka bir değişiklik yapmadan kodu doğru bir şekilde derlemek mümkün olmalıdır.

Koşullu kesme ifadesi neden bir derleyici hatasına neden oluyor?

Döngü erişilebilirlik kurallarında belirtildiği gibi, while döngüsü, ulaşılabilir bir kesme ifadesi içeriyorsa normal olarak tamamlanabilir. Bir ait erişilebilirlik kuralları yana ififadesinin ardından durumunu yapmayız maddede ifhiç dikkate böyle bir koşullu ififadedir sonra cümlesi her zaman erişilebilir kabul edilir.

Eğer breakulaşılabilir durumdaysa, döngüden sonraki kod bir kez daha ulaşılabilir kabul edilir. Döngüden sonra ani sonlandırmayla sonuçlanan ulaşılabilir bir kod olmadığından , yöntemin normal şekilde tamamlanabileceği düşünülür ve böylece derleyici bunu bir hata olarak işaretler.

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.