Bir while döngüsü kullanmadan en içteki istisnayı bulmak mı?


84

C # bir istisna attığında, bir iç istisnaya sahip olabilir. Yapmak istediğim, en içteki istisnayı veya başka bir deyişle, iç istisnası olmayan yaprak istisnasını elde etmek. Bunu bir süre döngüsünde yapabilirim:

while (e.InnerException != null)
{
    e = e.InnerException;
}

Ama bunun yerine bunu yapmak için kullanabileceğim bir tek astar olup olmadığını merak ediyordum.


4
Sınıflarımdan birinde bir istisna atıyorum, ancak sınıfım tüm istisnaları yutan ve kendi istisnalarını atan bir kütüphane tarafından kullanılıyor. Sorun şu ki, kütüphane istisnası çok geneldir ve problemi nasıl çözeceğimi öğrenmek için hangi özel istisnayı attığımı bilmem gerekiyor. İşleri daha da kötüleştirmek için, kütüphane kendi istisnasını birçok kez iç içe geçmiş, keyfi bir derinliğe fırlatacaktır. Yani örneğin fırlatacak LibraryException -> LibraryException -> LibraryException -> MyException. Benim istisnam her zaman zincirin sonuncusudur ve kendi iç istisnası yoktur.
Daniel T.

Oh, neden en içten indiğinizi anlıyorum, bunu kendim yaptıktan sonra (ve en içteki belirli bir türden türetilen türler gibi) ama halihazırda sahip olduklarınızla ilgili sorunun ne olduğunu anlamıyorum.
Jon Hanna

Oh, ne demek istediğini anlıyorum. Bunu yazmanın başka bir yolu olup olmadığını merak ediyordum. LINQNeredeyse her şeyi yapmaya o kadar alıştım ki , bir döngü yazmak zorunda olduğumda garip geliyor. Çoğunlukla veri erişimiyle çalışıyorum, bu nedenle yaptığım hemen ICollectionshemen her şey için çalışıyorum .
Daniel T.

@Daniel, LINQsadece kodun hala döngüsel olması gerektiği gerçeğini maskelemiyor mu? .NET'te gerçek küme tabanlı komutlar var mı?
Brad

6
En doğru cevap, (şu anda) yalnızca 2 oyla (Exception.GetBaseException ()) dibe yakın pusuda bekliyor. Bu, temelde sonsuza dek çerçeve içindedir. Akıl sağlığı kontrolü için batCattle'a teşekkürler.
Jay

Yanıtlar:



122

Exception.GetBaseException()Bu çözümlerle aynı şeyi yaptığına inanıyorum .

Uyarı: Çeşitli yorumlardan, bunun her zaman tam anlamıyla aynı şeyi yapmadığını ve bazı durumlarda yinelemeli / yinelemeli çözümün sizi daha da ileriye götüreceğini anladık. Varsayılanı geçersiz kılan belirli İstisna türleri sayesinde , genellikle hayal kırıklığı yaratacak şekilde tutarsız olan en içteki istisnadır. Bununla birlikte, belirli istisna türlerini yakalarsanız ve tuhaf olmadıklarından makul ölçüde emin olursanız (AggregateException gibi), o zaman meşru en içteki / en erken istisnayı almasını beklerim.


3
+1 Kıvrımlı çözümlerden kaçınmak için BCL'yi bilmek gibisi yoktur. Açıkçası, bu en doğru cevap.
Jay

4
Nedense, GetBaseException()ilk kök istisnasını döndürmedi.
Tarık

4
Glenn McElhoe, aslında GetBaseException () 'nın her zaman MSDN'nin genel olarak beklediğimizi ("bir veya daha fazla sonraki istisnanın temel nedeni olan İstisna") yapmadığını belirtti. AggregateException'da, aynı türdeki en düşük istisna ile sınırlıdır ve belki başkaları da vardır.
Josh Sutterfield

1
Sorunum için çalışmıyor. Bunun sağladığından birkaç seviyem daha var.
Giovanni B

2
GetBaseException()benim için işe yaramadı, çünkü en önemli istisna bir AggregateException.
Chris Ballance

22

InnerExceptions üzerinden döngü yapmak tek güvenilir yoldur.

Yakalanan istisna bir AggregateException ise, GetBaseException()yalnızca en içteki AggregateException'ı döndürür.

http://msdn.microsoft.com/en-us/library/system.aggregateexception.getbaseexception.aspx


1
İyi yakalama. Teşekkürler - bu, yanıtın bazı insanlar için işe yaramadığı yukarıdaki yorumları açıklıyor. Bu, MSDN'nin "sonraki bir veya daha fazla istisnanın temel nedeni" genel tanımının, türetilmiş sınıflar onu geçersiz kılabileceğinden, her zaman varsaydığınız gibi olmadığını açıkça ortaya koymaktadır. Gerçek kural, bir istisnalar zincirindeki tüm istisnaların GetBaseException () üzerinde "uyuşması" ve aynı nesneyi geri döndürmesidir, bu AggregateException için de geçerli gibi görünmektedir.
Josh Sutterfield

12

İç istisnaların ne kadar derinlemesine iç içe olduğunu bilmiyorsanız, bir döngü veya özyinelemenin etrafından dolaşmanın yolu yoktur.

Elbette, bunu soyutlayan bir uzatma yöntemi tanımlayabilirsiniz:

public static class ExceptionExtensions
{
    public static Exception GetInnermostException(this Exception e)
    {
        if (e == null)
        {
            throw new ArgumentNullException("e");
        }

        while (e.InnerException != null)
        {
            e = e.InnerException;
        }

        return e;
    }
}


2

Bazen birçok iç istisnanız olabilir (birçok istisnai durum). Bu durumda şunları yapmak isteyebilirsiniz:

List<Exception> es = new List<Exception>();
while(e.InnerException != null)
{
   es.add(e.InnerException);
   e = e.InnerException
}

1

Tam olarak tek satır değil ama yakın:

        Func<Exception, Exception> last = null;
        last = e => e.InnerException == null ? e : last(e.InnerException);

1

Aslında çok basit, kullanabilirsin Exception.GetBaseException()

Try
      //Your code
Catch ex As Exception
      MessageBox.Show(ex.GetBaseException().Message, My.Settings.MsgBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
End Try

Çözümünüz için teşekkürler! Birkaç kişi
Federico Navarrete

1
Ve bunun bir nedeni var! : P
Yoda

1

Döngü yapmanız ve döngü yapmanız gerekir, döngüyü ayrı bir işleve taşımak daha temizdir.

Bununla başa çıkmak için bir uzatma yöntemi oluşturdum. Exception.InnerException ve AggregateException.InnerExceptions 'ı takip ederek, belirtilen türün tüm iç özel durumlarının bir listesini döndürür.

Benim özel sorunumda, iç istisnaların peşinden gitmek her zamankinden daha karmaşıktı çünkü istisnalar, yansıtma yoluyla çağrılan sınıfların kurucuları tarafından fırlatılıyordu. Yakaladığımız istisna, TargetInvocationException türünde bir InnerException'a sahipti ve gerçekten bakmamız gereken istisnalar ağacın derinliklerine gömülmüştü.

public static class ExceptionExtensions
{
    public static IEnumerable<T> innerExceptions<T>(this Exception ex)
        where T : Exception
    {
        var rVal = new List<T>();

        Action<Exception> lambda = null;
        lambda = (x) =>
        {
            var xt = x as T;
            if (xt != null)
                rVal.Add(xt);

            if (x.InnerException != null)
                lambda(x.InnerException);

            var ax = x as AggregateException;
            if (ax != null)
            {
                foreach (var aix in ax.InnerExceptions)
                    lambda(aix);
            }
        };

        lambda(ex);

        return rVal;
    }
}

Kullanımı oldukça basittir. Örneğin, bir sorunla karşılaşıp karşılaşmadığımızı bilmek istiyorsanız

catch (Exception ex)
{
    var myExes = ex.innerExceptions<MyException>();
    if (myExes.Any(x => x.Message.StartsWith("Encountered my specific error")))
    {
        // ...
    }
}

Ne hakkında Exception.GetBaseException()?
Kiquenet

1

Bir yerde bir yardımcı program sınıfında bir yöntem oluşturmak için özyinelemeyi kullanabilirsiniz.

public Exception GetFirstException(Exception ex)
{
    if(ex.InnerException == null) { return ex; } // end case
    else { return GetFirstException(ex.InnerException); } // recurse
}

Kullanım:

try
{
    // some code here
}
catch (Exception ex)
{
    Exception baseException = GetFirstException(ex);
}

Uzantı yöntemi önerildi (iyi fikir @dtb)

public static Exception GetFirstException(this Exception ex)
{
    if(ex.InnerException == null) { return ex; } // end case
    else { return GetFirstException(ex.InnerException); } // recurse
}

Kullanım:

try
{
    // some code here
}
catch (Exception ex)
{
    Exception baseException = ex.GetFirstException();
}

Faydalı public static Exception GetOriginalException(this Exception ex) { if (ex.InnerException == null) return ex; return ex.InnerException.GetOriginalException(); }
Kiquenet

Geçerli değil AggregateException.InnerExceptionsmi?
Kiquenet

0

Bunu yapmanın başka bir yolu da GetBaseException()iki kez aramaktır:

Exception innermostException = e.GetBaseException().GetBaseException();

Bu işe yarar çünkü eğer o bir AggregateExceptionise, ilk çağrı sizi en içteki olmayana götürür, AggregateExceptiono zaman ikinci çağrı sizi bu istisnanın en içteki istisnasına götürür. İlk istisna bir değilse AggregateException, ikinci çağrı sadece aynı istisnayı döndürür.


0

Bununla karşılaştım ve istisna "yığını" ndaki tüm istisna mesajlarını listeleyebilmek istedim. Ben de bunu buldum.

public static string GetExceptionMessages(Exception ex)
{
    if (ex.InnerException is null)
        return ex.Message;
    else return $"{ex.Message}\n{GetExceptionMessages(ex.InnerException)}";
}
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.