সি # তে স্ট্যাক ট্রেস না হারিয়ে অভ্যন্তরীণ ধারণাটি কীভাবে পুনর্নবীকরণ করবেন?


305

প্রতিবিম্বের মাধ্যমে আমি ফোন করছি, এমন একটি পদ্ধতি যা ব্যতিক্রম ঘটতে পারে। মোড়কের প্রতিবিম্বটি চারপাশে না রেখে আমি কীভাবে আমার কলারের কাছে এই ব্যতিক্রমটি পাস করতে পারি?
আমি অভ্যন্তরীণ ধারণাটি পুনর্বিবেচনা করছি, তবে এটি স্ট্যাক ট্রেসটিকে ধ্বংস করে।
উদাহরণ কোড:

public void test1()
{
    // Throw an exception for testing purposes
    throw new ArgumentException("test1");
}

void test2()
{
    try
    {
        MethodInfo mi = typeof(Program).GetMethod("test1");
        mi.Invoke(this, null);
    }
    catch (TargetInvocationException tiex)
    {
        // Throw the new exception
        throw tiex.InnerException;
    }
}

1
এটি করার জন্য আরও একটি উপায় আছে যার কোনও ভুডুর প্রয়োজন হয় না। এখানে উত্তরটি একবার দেখুন: stackoverflow.com/questions/15668334/…
টিমোথি শিল্ডস

গতিশীল নামে পরিচিত পদ্ধতিতে ছড়িয়ে দেওয়া ব্যতিক্রমটি "ব্যাতিক্রমের অনুরোধটি ছুঁড়ে দেওয়া হয়েছে" ব্যতিক্রমের অভ্যন্তরীণ ব্যতিক্রম। এটির নিজস্ব স্ট্যাক ট্রেস রয়েছে। উদ্বিগ্ন হওয়ার মতো সত্যিকারের আর কিছুই নেই।
আজ

উত্তর:


481

ইন .NET 4.5 সেখানে এখন ExceptionDispatchInfoবর্গ।

এটি আপনাকে স্ট্যাক-ট্রেস পরিবর্তন না করে একটি ব্যতিক্রম ক্যাপচার করতে এবং এটি পুনরায় ছুঁড়ে ফেলার অনুমতি দেয়:

try
{
    task.Wait();
}
catch(AggregateException ex)
{
    ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}

এটি কেবলমাত্র নয়, কোনও ব্যতিক্রম নিয়ে কাজ করে AggregateException

এটি awaitসি # ভাষার বৈশিষ্ট্যটির কারণে প্রবর্তিত হয়েছিল , যা সংশ্লেষীয় AggregateExceptionভাষা বৈশিষ্ট্যগুলিকে আরও সুসংগত ভাষার বৈশিষ্ট্যগুলির মতো করে তুলতে দৃষ্টান্তগুলি থেকে অভ্যন্তরীণ ব্যতিক্রমগুলি মুছে ফেলে ।


11
একটি ব্যাতিক্রমের জন্য ভাল প্রার্থী। রিথ্রো () এক্সটেনশন পদ্ধতি?
nmarler

8
নোট করুন যে এক্সপশনডিশপচইনফো ক্লাসটি সিস্টেম.রুনটাইম.এক্সপেশন সার্ভিসেস নেমস্পেসে রয়েছে এবং নেট .৪.৪ এর পূর্বে উপলভ্য নয়।
ইয়োও

53
আপনি throw;। থ্রো () লাইনের পরে একটি নিয়মিত লাগাতে পারেন , কারণ সংকলকটি এটি জানতে পারবে না। থ্র () সর্বদা একটি ব্যতিক্রম ছুঁড়ে ফেলে। throw;ফলস্বরূপ কখনই ডাকা হবে না, তবে আপনার পদ্ধতিটিতে কোনও রিটার্ন অবজেক্টের প্রয়োজন হয় বা অ্যাসিঙ্ক ফাংশন থাকলে কমপক্ষে কমপাইলার অভিযোগ করবে না।
টড

5
@ টড্রিস এই প্রশ্নটি বিশেষত অভ্যন্তরীণ ব্যতিক্রমগুলির পুনর্বিবেচনা সম্পর্কে, যা বিশেষভাবে পরিচালনা করা যায় না throw;। আপনি যদি throw ex.InnerException;স্ট্যাক-ট্রেস ব্যবহার করেন তবে পুনরায় নিক্ষেপ করা হবে এমন বিন্দুতে পুনরায় আরম্ভ করা হবে।
পল টার্নার

5
@ এমিতঝাExceptionDispatchInfo.Capture(ex.InnerException ?? ex).Throw();
বেদরান

86

এটা তোলে হয় প্রতিফলন ছাড়া rethrowing আগে স্ট্যাক ট্রেস সংরক্ষণ করা সম্ভব:

static void PreserveStackTrace (Exception e)
{
    var ctx = new StreamingContext  (StreamingContextStates.CrossAppDomain) ;
    var mgr = new ObjectManager     (null, ctx) ;
    var si  = new SerializationInfo (e.GetType (), new FormatterConverter ()) ;

    e.GetObjectData    (si, ctx)  ;
    mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData
    mgr.DoFixups       ()         ; // ObjectManager calls SetObjectData

    // voila, e is unmodified save for _remoteStackTraceString
}

InternalPreserveStackTraceক্যাশেড ডেলিগেটের মাধ্যমে কল করার তুলনায় এটি চক্রের প্রচুর অপচয় করে , তবে কেবলমাত্র জনসাধারণের কার্যকারিতার উপর নির্ভর করার সুবিধা রয়েছে। স্ট্যাক-ট্রেস সংরক্ষণের ফাংশনগুলির জন্য এখানে কয়েকটি সাধারণ ব্যবহারের ধরণ রয়েছে:

// usage (A): cross-thread invoke, messaging, custom task schedulers etc.
catch (Exception e)
{
    PreserveStackTrace (e) ;

    // store exception to be re-thrown later,
    // possibly in a different thread
    operationResult.Exception = e ;
}

// usage (B): after calling MethodInfo.Invoke() and the like
catch (TargetInvocationException tiex)
{
    PreserveStackTrace (tiex.InnerException) ;

    // unwrap TargetInvocationException, so that typed catch clauses 
    // in library/3rd-party code can work correctly;
    // new stack trace is appended to existing one
    throw tiex.InnerException ;
}

দেখতে দুর্দান্ত লাগছে, এই ফাংশনগুলি চালানোর পরে কী হওয়ার দরকার?
vdboor

2
প্রকৃতপক্ষে, এটি চাওয়ার চেয়ে খুব ধীর নয় InternalPreserveStackTrace(10000 পুনরাবৃত্তির সাথে প্রায় 6% ধীর)। প্রতিবিম্ব দ্বারা সরাসরি ক্ষেত্রগুলি অ্যাক্সেস করা InternalPreserveStackTrace
প্রার্থনার

1
আমি e.Dataস্ট্রিং বা একটি অনন্য অবজেক্ট কী দিয়ে অভিধানটি ব্যবহার করার পরামর্শ দিচ্ছি ( static readonly object myExceptionDataKey = new object ()তবে আপনি যদি কোথাও ব্যতিক্রমকে সিরিয়ালাইজ করতে হয় তবে এটি করবেন না)। সংশোধন করা এড়ান e.Message, কারণ আপনার কোথাও কোড থাকতে পারে যা পার্স করে e.Message। পার্সিং e.Messageদুষ্ট, তবে এর বাইরে অন্য কোনও পছন্দ থাকতে পারে না, যেমন আপনার যদি কোনও তৃতীয় পক্ষের লাইব্রেরি দুর্বল ব্যতিক্রম অভ্যাসের সাথে ব্যবহার করতে হয়।
আন্তন টিখি

10
সিরিয়ালাইজেশন কর্টারের না থাকলে কাস্টম ব্যতিক্রমগুলির জন্য ডো ফিক্সআপস বিরতি দেয়
রাসেল্যান্ডার

3
যদি ব্যতিক্রমটিতে সিরিয়ালাইজেশন কনস্ট্রাক্টর না থাকে তবে প্রস্তাবিত সমাধানটি কাজ করে না। আমি স্ট্যাকওভারফ্লো . com/a/4557183/209727 এ প্রস্তাবিত সমাধানটি ব্যবহার করার পরামর্শ দিচ্ছি যা কোনও ক্ষেত্রেই ভাল কাজ করে। .NET 4.5 এর জন্য এক্সেপশনডিসপ্যাচআইনফো ক্লাসটি ব্যবহার করতে বিবেচনা করুন।
ডেভিড ইকার্দি

33

আমি মনে করি আপনার সেরা বাজি হ'ল এটি কেবল আপনার ক্যাচ ব্লকে রেখে দেওয়া:

throw;

এবং তারপরে পরে অযৌক্তিক ধারণাটি বের করুন।


21
বা চেষ্টা পুরোপুরি মুছে ফেলুন / ধরুন।
ড্যানিয়েল আরউইকার

6
@Earwicker। কল / স্ট্যাক ব্যতিক্রম প্রচারের আগে ক্লিনআপ কোড প্রয়োজন এমন ক্ষেত্রে উপেক্ষা করে সাধারণভাবে চেষ্টা / ধরা অপসারণ করা ভাল সমাধান নয়।
জর্ডান

12
@ জর্ডান - ক্লিন আপ কোডটি শেষ অবধি ব্লকগুলিতে হওয়া উচিত ক্যাচ ব্লক নয়
পাওলো

17
@ পাওলো - যদি প্রতিটি ক্ষেত্রে এটি কার্যকর করার কথা হয় তবে হ্যাঁ। যদি কেবলমাত্র ব্যর্থতার ক্ষেত্রে এটি কার্যকর করা হয় বলে মনে করা হয়, না।
চিককোডোরো

4
মনে রাখবেন যে ইন্টারনালপ্রিজারস্ট্যাকট্রেস থ্রেডটি নিরাপদ নয়, সুতরাং এই ব্যতিক্রমগুলির মধ্যে যদি আপনার 2 টি থ্রেড থাকে ... তবে godশ্বর আমাদের সকলের জন্য দয়া করুন।
রব

13
public static class ExceptionHelper
{
    private static Action<Exception> _preserveInternalException;

    static ExceptionHelper()
    {
        MethodInfo preserveStackTrace = typeof( Exception ).GetMethod( "InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic );
        _preserveInternalException = (Action<Exception>)Delegate.CreateDelegate( typeof( Action<Exception> ), preserveStackTrace );            
    }

    public static void PreserveStackTrace( this Exception ex )
    {
        _preserveInternalException( ex );
    }
}

আপনি এটি ছোঁড়ার আগে আপনার ব্যতিক্রমগুলিতে এক্সটেনশন পদ্ধতিটি কল করুন, এটি মূল স্ট্যাক ট্রেস সংরক্ষণ করবে।


জেনে রাখুন যে নেট নেট 4.0.০ এ, ইন্টার্নালপ্রিজারস্ট্যাকট্রেস এখন একটি অনিচ্ছুক - রিফ্লেক্টরে দেখুন এবং আপনি দেখতে পাবেন যে পদ্ধতিটি সম্পূর্ণ ফাঁকা!
স্যামুয়েল জ্যাক

স্ক্র্যাচ যে: আমি আরসির দিকে তাকিয়ে ছিলাম: বিটাতে, তারা বাস্তবায়নটি আবার ফিরিয়ে দিয়েছে!
স্যামুয়েল জ্যাক

3
প্রস্তাবনা: প্রিজারস্ট্যাকট্রেস পরিবর্তন করে প্রাক্তনকে ফিরিয়ে আনুন - তারপরে একটি ব্যতিক্রম ছুঁড়ে দেওয়ার জন্য আপনি কেবল বলতে পারেন: নিক্ষেপ করুন প্রিজারস্ট্যাকট্রেস ();
সাইমন_উইভার

কেন ব্যবহার করবেন Action<Exception>? এখানে স্থিতিশীল পদ্ধতি
কিকুইনেট

13

কেউই ExceptionDispatchInfo.Capture( ex ).Throw()কোনও সমভূমি এবং সমভূমির পার্থক্য ব্যাখ্যা করতে throwপারেনি, সুতরাং এটি এখানে।

ধরা পড়া ব্যতিক্রমটি পুনর্বিবেচনার পুরো উপায়টি হ'ল ExceptionDispatchInfo.Capture( ex ).Throw()(কেবলমাত্র নেট .৪.৪ থেকে পাওয়া যায়)।

নীচে এটি পরীক্ষা করার জন্য প্রয়োজনীয় কেসগুলি রয়েছে:

1।

void CallingMethod()
{
    //try
    {
        throw new Exception( "TEST" );
    }
    //catch
    {
    //    throw;
    }
}

2।

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        ExceptionDispatchInfo.Capture( ex ).Throw();
        throw; // So the compiler doesn't complain about methods which don't either return or throw.
    }
}

3।

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch
    {
        throw;
    }
}

4।

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        throw new Exception( "RETHROW", ex );
    }
}

কেস 1 এবং কেস 2 আপনাকে একটি স্ট্যাক ট্রেস দেবে যেখানে CallingMethodপদ্ধতির সোর্স কোড লাইন নম্বরটি লাইনের লাইন নম্বর throw new Exception( "TEST" )

তবে কেস 3 আপনাকে স্ট্যাক ট্রেস দেবে যেখানে CallingMethodপদ্ধতির সোর্স কোড লাইন নম্বরটি throwকলটির লাইন নম্বর । এর অর্থ throw new Exception( "TEST" )হ'ল লাইনটি যদি অন্য ক্রিয়াকলাপ দ্বারা ঘিরে থাকে তবে কোন লাইন নম্বরটিতে ব্যতিক্রমটি আসলে নিক্ষেপ করা হয়েছিল তা আপনার কোনও ধারণা নেই।

কেস 4 কেস 2 এর সাথে সমান কারণ মূল ব্যতিক্রমের লাইন নম্বরটি সংরক্ষণ করা হলেও এটি সত্যিকারের পুনর্নবী নয় কারণ এটি আসল ব্যতিক্রমের ধরণের পরিবর্তন করে।


4
আমি সর্বদা ভেবেছিলাম যে 'থ্রো' স্ট্যাকট্রেস পুনরায় সেট করেনি ('থ্রো ই' এর বিপরীতে)।
জেস্পার ম্যাথিজেন

@ জেস্পার ম্যাথিয়েসেন আমাকে ভুল হতে পারে, তবে আমি শুনেছি ব্যতিক্রম একই ফাইলটিতে ফেলে দেওয়া হয়েছিল কিনা তা নির্ভর করে। যদি এটি একই ফাইল হয় তবে স্ট্যাকের ট্রেস নষ্ট হবে, এটি যদি অন্য কোনও ফাইল হয় তবে এটি সংরক্ষণ করা হবে।
jahu

10

আরও প্রতিবিম্ব ...

catch (TargetInvocationException tiex)
{
    // Get the _remoteStackTraceString of the Exception class
    FieldInfo remoteStackTraceString = typeof(Exception)
        .GetField("_remoteStackTraceString",
            BindingFlags.Instance | BindingFlags.NonPublic); // MS.Net

    if (remoteStackTraceString == null)
        remoteStackTraceString = typeof(Exception)
        .GetField("remote_stack_trace",
            BindingFlags.Instance | BindingFlags.NonPublic); // Mono

    // Set the InnerException._remoteStackTraceString
    // to the current InnerException.StackTrace
    remoteStackTraceString.SetValue(tiex.InnerException,
        tiex.InnerException.StackTrace + Environment.NewLine);

    // Throw the new exception
    throw tiex.InnerException;
}

মনে রাখবেন যে এটি যে কোনও সময় ভেঙে যেতে পারে, কারণ ব্যক্তিগত ক্ষেত্রগুলি API এর অংশ নয়। মনো ব্যাগজিলা সম্পর্কে আরও আলোচনা দেখুন ।


28
এটি একটি সত্যই, সত্যই খারাপ ধারণা, কারণ এটি কাঠামোর শ্রেণিগুলি সম্পর্কে অভ্যন্তরীণ অননুমোদিত বিবরণগুলির উপর নির্ভর করে।
ড্যানিয়েল আরউইকার

1
দেখা যাচ্ছে যে প্রতিচ্ছবি ছাড়াই স্ট্যাক ট্রেস সংরক্ষণ করা সম্ভব, নীচে দেখুন।
আন্তন টিখি

1
অভ্যন্তরীণ InternalPreserveStackTraceপদ্ধতিটি কল করা ভাল হবে, যেহেতু এটি একই কাজ করে এবং ভবিষ্যতে পরিবর্তনের সম্ভাবনাও কম ...
টমাস লেভস্ক

1
প্রকৃতপক্ষে, এটি আরও খারাপ হবে, যেহেতু মনো-তে ইন্টার্নালপ্রিজারস্ট্যাকট্র্যাসের অস্তিত্ব নেই।
স্কোলিমা

5
@ ড্যানিয়েল - এটি সত্যিই, সত্যই, সত্যিই খারাপ ধারণা ছোঁড়ার জন্য; স্ট্যাকট্রেসটি পুনরায় সেট করতে যখন প্রতিটি। নেট বিকাশকারী এটি বিশ্বাস করতে প্রশিক্ষিত হয়। এটি একটি সত্যই, সত্যই, সত্যই খারাপ জিনিস যদি আপনি কোনও নালরফেরিয়েন্সিপেশনের উত্স খুঁজে না পান এবং কোনও গ্রাহক / অর্ডার হারাবেন কারণ আপনি এটি খুঁজে পেতে পারেন না। আমার জন্য এটি 'অনিবন্ধিত বিবরণ' এবং স্পষ্টতই মনোগুলি ট্রাম্প করে।
সাইমন_উইভার

10

প্রথম: টার্গেটআইভোকেশন এক্সেপশন হারাবেন না - আপনি জিনিসগুলি ডিবাগ করতে চাইলে এটি মূল্যবান তথ্য।
দ্বিতীয়: আপনার নিজের ব্যতিক্রম প্রকারের মধ্যে টিআইই অভ্যন্তরীণ ধারণা হিসাবে মোড়ুন এবং একটি অরিজিনাল এক্সসেপশন সম্পত্তি রাখুন যা আপনার প্রয়োজনের সাথে লিঙ্ক করে (এবং পুরো কলস্ট্যাকটি অক্ষত রাখুন)।
তৃতীয়: আপনার পদ্ধতি থেকে টিআইই বুদ্বুদকে ছেড়ে দিন।


5

বন্ধুরা, আপনি শীতল .. আমি শীঘ্রই একটি necromancer হতে চলেছি।

    public void test1()
    {
        // Throw an exception for testing purposes
        throw new ArgumentException("test1");
    }

    void test2()
    {
            MethodInfo mi = typeof(Program).GetMethod("test1");
            ((Action)Delegate.CreateDelegate(typeof(Action), mi))();

    }

1
দুর্দান্ত ধারণা, তবে আপনি যে কোডটি কল করেন তা সবসময় নিয়ন্ত্রণ করেন না .Invoke()
আন্তন টিখি

1
এবং আপনি সংকলন সময়ে সর্বদা আর্গুমেন্ট / ফলাফলের ধরণগুলি জানেন না।
রোমান স্টারকভ

3

অ্যান্টিপোরাল নমুনা কোড যা ব্যতিক্রম সিরিয়ালাইজেশন / ডিসরিয়ালাইজেশন ব্যবহার করে। এটির জন্য সত্যিকারের ব্যতিক্রম প্রকারটি সিরিয়ালাইজযোগ্য হতে হবে না। এছাড়াও এটি কেবল সর্বজনীন / সুরক্ষিত পদ্ধতি ব্যবহার করে।

    static void PreserveStackTrace(Exception e)
    {
        var ctx = new StreamingContext(StreamingContextStates.CrossAppDomain);
        var si = new SerializationInfo(typeof(Exception), new FormatterConverter());
        var ctor = typeof(Exception).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }, null);

        e.GetObjectData(si, ctx);
        ctor.Invoke(e, new object[] { si, ctx });
    }

আসল ব্যতিক্রম ধরণের ক্রমিকায়িত হওয়ার দরকার নেই?
কুইকিনেট 25'18

3

পল টার্নারস উত্তরের ভিত্তিতে আমি একটি এক্সটেনশন পদ্ধতি তৈরি করেছি

    public static Exception Capture(this Exception ex)
    {
        ExceptionDispatchInfo.Capture(ex).Throw();
        return ex;
    }

return exIST পৌঁছে না কিন্তু সুবিধা যে আমি ব্যবহার করতে পারেন throw ex.Capture()একটি এক মাছ ধরার নৌকা তাই কম্পাইলার একটি পুনরুজ্জীবিত করবেন না not all code paths return a valueত্রুটি।

    public static object InvokeEx(this MethodInfo method, object obj, object[] parameters)
    {
        {
            return method.Invoke(obj, parameters);
        }
        catch (TargetInvocationException ex) when (ex.InnerException != null)
        {
            throw ex.InnerException.Capture();
        }
    }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.