নেস্টেড চেষ্টা ব্লক এড়াতে প্যাটার্ন?


114

এমন একটি পরিস্থিতি বিবেচনা করুন যেখানে আমার কাছে গণনা করার তিনটি (বা আরও) উপায় রয়েছে, যার মধ্যে প্রতিটি ব্যতিক্রম ব্যর্থ হতে পারে। সাফল্য পাওয়া কোনওটি না পাওয়া পর্যন্ত প্রতিটি গণনার চেষ্টা করার জন্য, আমি নিম্নলিখিতটি করে চলেছি:

double val;

try { val = calc1(); }
catch (Calc1Exception e1)
{ 
    try { val = calc2(); }
    catch (Calc2Exception e2)
    {
        try { val = calc3(); }
        catch (Calc3Exception e3)
        {
            throw new NoCalcsWorkedException();
        }
    }
}

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


আপনি গণনা সম্পর্কে কিছু বিবরণ অন্তর্ভুক্ত করতে পারেন?
জেমস জনসন

2
এগুলি মূলত পিডিই সমাধান / সংলগ্ন করার বিভিন্ন পদ্ধতি। তারা একটি তৃতীয় পক্ষের লাইব্রেরি থেকে এসেছে, তাই আমি ত্রুটি কোডগুলি বা শূন্য করতে তাদের পরিবর্তন করতে পারি না। আমি সর্বোত্তমভাবে করতে পারি হ'ল প্রতিটি পদ্ধতিতে পৃথকভাবে মোড়ানো।
jjoelson

ক্যালক পদ্ধতিগুলি কি আপনার প্রকল্পের অংশ (তৃতীয় পক্ষের লাইব্রেরির পরিবর্তে)? যদি তা হয় তবে আপনি যে যুক্তিটি ব্যাতিক্রম ছুঁড়ে ফেলতে পারেন এবং কোন ক্যালক পদ্ধতিটি কল করা দরকার তা নির্ধারণ করতে এটি ব্যবহার করতে পারেন।
ক্রিস

1
এর জন্য আরও একটি ব্যবহারের কেস রয়েছে যা আমি জাভাতে এসে পৌঁছেছি - আমাকে Stringএকটি Dateব্যবহারের জন্য পার্স করা দরকার এবং একটি SimpleDateFormat.parseব্যতিক্রম ছুঁড়ে মারার পরে আমাকে আরও কয়েকটি বিভিন্ন বিন্যাস চেষ্টা করতে হবে।
দুর্বল পরিবর্তনশীল

উত্তর:


125

যতদূর সম্ভব, কন্ট্রোল প্রবাহ বা অবাস্তব পরিস্থিতির জন্য ব্যতিক্রম ব্যবহার করবেন না।

তবে আপনার প্রশ্নের সরাসরি উত্তর দেওয়ার জন্য (সমস্ত ব্যতিক্রম-ধরণগুলি একই রকম ধরে নিলে):

Func<double>[] calcs = { calc1, calc2, calc3 };

foreach(var calc in calcs)
{
   try { return calc(); }
   catch (CalcException){  }
} 

throw new NoCalcsWorkedException();

15
এই অনুমান Calc1Exception, Calc2Exceptionএবং Calc3Exceptionএকটি সাধারণ বেস বর্গ ভাগ করুন।
উইজার্ড

3
সর্বোপরি, তিনি একটি সাধারণ স্বাক্ষর ধরে নিয়েছেন - এটি আসলে খুব বেশি দূরে নয়। ভাল উত্তর।
টমটম

1
এছাড়াও, আমি continueক্যাচ ব্লকে এবং ক্যাচ ব্লকের breakপরে যুক্ত করেছি যাতে কোনও গণনা কাজ করার সময় লুপটি শেষ হয় (এই বিটের জন্য
লিরিককে

6
+1 কেবল কারণ এটি "নিয়ন্ত্রণ প্রবাহের জন্য ব্যতিক্রম ব্যবহার করবেন না" বলেছে যদিও আমি "যতদূর সম্ভব" এর পরিবর্তে "কখনও কখনও না" ব্যবহার করতাম।
বিল কে

1
@ জাজোয়েলসন: (এবং কোনও বিবৃতি নেই ) এর মধ্যে breakনিম্নলিখিত বিবৃতিটি কিছুটা মূর্খবাদী হতে পারে। calc();trycontinue
অ্যাডাম রবিনসন 21

38

কেবল একটি "বাক্সের বাইরে" বিকল্প প্রস্তাব দেওয়ার জন্য, কীভাবে পুনরাবৃত্তির কাজটি করা যায় ...

//Calling Code
double result = DoCalc();

double DoCalc(int c = 1)
{
   try{
      switch(c){
         case 1: return Calc1();
         case 2: return Calc2();
         case 3: return Calc3();
         default: return CalcDefault();  //default should not be one of the Calcs - infinite loop
      }
   }
   catch{
      return DoCalc(++c);
   }
}

উল্লেখ্য: আমি এই বলে যে এই ভাল উপায় কাজ শেষ করার জন্য কোন উপায়ে am, শুধু একটি বিভিন্ন উপায়


6
আমাকে একবার একটি ভাষায় "অন ত্রুটি পুনরায় শুরু করুন" প্রয়োগ করতে হয়েছিল এবং আমি যে কোডটি উত্পন্ন করেছিলাম তা অনেকটা এ জাতীয় দেখাচ্ছে।
জ্যাকব ক্রোল

4
লুপ তৈরি করতে কখনও কখনও স্যুইচ স্টেটমেন্ট ব্যবহার করবেন না।
জেফ ফেরল্যান্ড

3
লুপিংয়ের জন্য স্যুইচ স্টেটমেন্ট রাখা বজায় রাখা যায় না
মোহাম্মদ আবেদ

1
আমি জানি আমার উত্তর সর্বাধিক দক্ষ কোড নয় তবে এরপরে আবার এই ধরণের জিনিসটির জন্য ট্র্যাচ / ক্যাচ ব্লক ব্যবহার করা যাই হোক না কেন সর্বোত্তম উপায় নয়। দুর্ভাগ্যক্রমে, ওপি একটি তৃতীয় পক্ষের লাইব্রেরি ব্যবহার করছে এবং সাফল্য নিশ্চিত করতে তাদের যথাসাধ্য চেষ্টা করতে হবে। আদর্শভাবে, ইনপুটটি প্রথমে যাচাই করা হয়েছে এবং এটি ব্যর্থ হবে না তা নিশ্চিত করার জন্য নির্বাচিত সঠিক গণনা কার্যক্রমে - অবশ্যই, আপনি কেবল সেই
চেষ্টাটি

1
return DoCalc(c++)এর সমতুল্য return DoCalc(c)- পোস্ট-ইনক্রিমেন্ট মান আরও গভীরতর হবে না। এটিকে কাজ করতে (এবং আরও অস্পষ্টতার পরিচয় দিতে) এটি আরও বেশি পছন্দ হতে পারে return DoCalc((c++,c))
আর্টুর সিজ্জাকা

37

আপনি এই জাতীয় পদ্ধতিতে বাসা বাঁধতে সমতল করতে পারেন:

private double calcStuff()
{
  try { return calc1(); }
  catch (Calc1Exception e1)
  {
    // Continue on to the code below
  }

  try { return calc2(); }
  catch (Calc2Exception e1)
  {
    // Continue on to the code below
  }

  try { return calc3(); }
  catch (Calc3Exception e1)
  {
    // Continue on to the code below
  }

  throw new NoCalcsWorkedException();
}

তবে আমি আসল সন্দেহ করি নকশা সমস্যাটি হ'ল তিনটি পৃথক পদ্ধতির অস্তিত্ব যা মূলত একই জিনিস (কলারের দৃষ্টিকোণ থেকে) করে তবে ভিন্ন, সম্পর্কযুক্ত ব্যতিক্রম ছুঁড়ে।

এই অভিমানী হয় তিন ব্যতিক্রম হয় সম্পর্কহীন। যদি তাদের সকলের একটি সাধারণ বেস শ্রেণি থাকে তবে অ্যানির পরামর্শ অনুসারে একটি একক ক্যাচ ব্লক সহ একটি লুপ ব্যবহার করা ভাল।


1
+1: এটি সমস্যাটির সবচেয়ে পরিষ্কার, সবচেয়ে অ-বাজে সমাধান। আমি এখানে অন্যান্য সমাধানগুলি দেখতে কেবল আইএমও বুদ্ধিমান হওয়ার চেষ্টা করছি। ওপি যেমন বলেছে, তিনি এপিআই লিখেনি তাই ছুঁড়ে যাওয়া ব্যতিক্রমগুলি নিয়ে সে আটকে গেছে।
নাট সি কে 21

19

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

bool TryCalculate(out double paramOut)
{
  try
  {
    // do some calculations
    return true;
  }
  catch(Exception e)
  { 
     // do some handling
    return false;
  }

}

double calcOutput;
if(!TryCalc1(inputParam, out calcOutput))
  TryCalc2(inputParam, out calcOutput);

আরেকটি প্রকরণ যা চেষ্টা করা প্যাটার্নটি ব্যবহার করে এবং নেস্টের পরিবর্তে পদ্ধতির তালিকার সংমিশ্রণ করা হয় যদি:

internal delegate bool TryCalculation(out double output);

TryCalculation[] tryCalcs = { calc1, calc2, calc3 };

double calcOutput;
foreach (var tryCalc in tryCalcs.Where(tryCalc => tryCalc(out calcOutput)))
  break;

এবং যদি ভবিষ্যদ্বাণীটি কিছুটা জটিল হয় তবে আপনি এটি সহজ করে তুলতে পারেন:

        foreach (var tryCalc in tryCalcs)
        {
            if (tryCalc(out calcOutput)) break;
        }

সত্যিই, আমি মনে করি এটি কেবল অপ্রয়োজনীয় বিমূর্ততা সৃষ্টি করে। এটি কোনও ভয়াবহ সমাধান নয়, তবে বেশিরভাগ ক্ষেত্রে আমি এটি ব্যবহার করব না।
ব্যবহারকারী 606723

যদি আপনি ব্যতিক্রম প্রকারের বিষয়ে চিন্তা না করেন এবং আপনি কেবল শর্তসাপেক্ষ কোডটি পরিচালনা করতে চান .. সুতরাং বিমূর্ততা এবং রক্ষণাবেক্ষণের ক্ষেত্রে অবশ্যই এটি সফল হয়েছে কিনা তা ফেরতের সাথে শর্তাধীন পদ্ধতিতে রূপান্তর করা ভাল is আপনি স্পষ্ট বর্ণনামূলক পদ্ধতির সাথে ব্যতিক্রম হ্যান্ডলিং অগোছালো সিনট্যাক্সটি লুকিয়ে রাখেন .. তবে এটি নিয়মিত শর্তাধীন পদ্ধতি হিসাবে আপনার কোড এটিকে পরিচালনা করবে।
মোহাম্মদ আবেদ

আমি পয়েন্টগুলি জানি, এবং সেগুলি বৈধ। যাইহোক, প্রায় সর্বত্র এই ধরণের বিমূর্ততা (গোপনীয়তা / জটিলতা লুকিয়ে রাখার) ব্যবহার করার সময় এটি হাস্যকর হয়ে ওঠে এবং সফ্টওয়্যারটির টুকরোটি বোঝার জন্য যা এটি আরও কঠিন হয়ে যায়। যেমনটি আমি বলেছি, এটি কোনও ভয়ঙ্কর সমাধান নয়, তবে আমি এটিকে হালকাভাবে ব্যবহার করব না।
ব্যবহারকারী 606723

9

আপনার গণনা ফাংশনে প্রতিনিধিদের একটি তালিকা তৈরি করুন এবং তারপরে চক্রটি চালানোর জন্য কিছুক্ষণ লুপ করুন:

List<Func<double>> calcMethods = new List<Func<double>>();

// Note: I haven't done this in a while, so I'm not sure if
// this is the correct syntax for Func delegates, but it should
// give you an idea of how to do this.
calcMethods.Add(new Func<double>(calc1));
calcMethods.Add(new Func<double>(calc2));
calcMethods.Add(new Func<double>(calc3));

double val;
for(CalcMethod calc in calcMethods)
{
    try
    {
        val = calc();
        // If you didn't catch an exception, then break out of the loop
        break;
    }
    catch(GenericCalcException e)
    {
        // Not sure what your exception would be, but catch it and continue
    }

}

return val; // are you returning the value?

এটি আপনাকে কীভাবে করতে হবে তার একটি সাধারণ ধারণা দেওয়া উচিত (অর্থাত্ এটি কোনও সঠিক সমাধান নয়)।


1
অবশ্যই ব্যতীত আপনার সাধারণত কখনই ধরা উচিত নয় Exception। ;)
ডেকাফ

@ ডেইকাফ যেমনটি আমি বলেছিলাম: "এটি কীভাবে করবেন (তার সঠিক সমাধান নয়) এর একটি সাধারণ ধারণা আপনাকে দেওয়া উচিত।" সুতরাং ওপিতে উপযুক্ত ব্যতিক্রম যাই হ'ল তা ধরতে পারে ... জেনেরিক ধরার দরকার নেই Exception
কিরিল

হ্যাঁ, দুঃখিত, কেবল এটি বের করার প্রয়োজনীয়তা অনুভব করেছেন।
ডেকাফ

1
@ ডেকাফ, এটি তাদের পক্ষে বৈধ স্পষ্টতা যাঁরা সেরা অনুশীলনের সাথে পরিচিত নন। ধন্যবাদ :)
কিরিল

9

এটিকে ... MONADS এর মতো কাজের মতো দেখাচ্ছে! বিশেষতঃ দ্য মোনড এখানে বর্ণিত হিসাবে সম্ভবত monad দিয়ে শুরু করুন । তারপরে কিছু এক্সটেনশন পদ্ধতি যুক্ত করুন। আপনি বর্ণনা করার সাথে সাথে আমি এই এক্সটেনশন পদ্ধতিগুলি বিশেষত লিখেছি। মনাদাদের সম্পর্কে দুর্দান্ত জিনিস হ'ল আপনি নিজের অবস্থার জন্য প্রয়োজনীয় প্রসারিত পদ্ধতিগুলি লিখতে পারেন।

public static Maybe<T> TryGet<T>(this Maybe<T> m, Func<T> getFunction)
{
    // If m has a value, just return m - we want to return the value
    // of the *first* successful TryGet.
    if (m.HasValue)
    {
        return m;
    }

    try
    {
        var value = getFunction();

        // We were able to successfully get a value. Wrap it in a Maybe
        // so that we can continue to chain.
        return value.ToMaybe();
    }
    catch
    {
        // We were unable to get a value. There's nothing else we can do.
        // Hopefully, another TryGet or ThrowIfNone will handle the None.
        return Maybe<T>.None;
    }
}

public static Maybe<T> ThrowIfNone<T>(
    this Maybe<T> m,
    Func<Exception> throwFunction)
{
    if (!m.HasValue)
    {
        // If m does not have a value by now, give up and throw.
        throw throwFunction();
    }

    // Otherwise, pass it on - someone else should unwrap the Maybe and
    // use its value.
    return m;
}

এটি এর মতো ব্যবহার করুন:

[Test]
public void ThrowIfNone_ThrowsTheSpecifiedException_GivenNoSuccessfulTryGet()
{
    Assert.That(() =>
        Maybe<double>.None
            .TryGet(() => { throw new Exception(); })
            .TryGet(() => { throw new Exception(); })
            .TryGet(() => { throw new Exception(); })
            .ThrowIfNone(() => new NoCalcsWorkedException())
            .Value,
        Throws.TypeOf<NoCalcsWorkedException>());
}

[Test]
public void Value_ReturnsTheValueOfTheFirstSuccessfulTryGet()
{
    Assert.That(
        Maybe<double>.None
            .TryGet(() => { throw new Exception(); })
            .TryGet(() => 0)
            .TryGet(() => 1)
            .ThrowIfNone(() => new NoCalcsWorkedException())
            .Value,
        Is.EqualTo(0));
}

আপনি যদি নিজেকে এই ধরণের গণনাগুলি প্রায়শই নিজেকে করতে দেখেন তবে সম্ভবত আপনার কোডের পঠনযোগ্যতা বাড়ানোর সময় মনড আপনার বয়লারপ্লেট কোডটি লিখতে হবে reduce


2
আমি এই সমাধান ভালোবাসি। যাইহোক, এটি যে কারও কাছে ইতিপূর্বে মনদেজের সংস্পর্শে আসেনি, তার পক্ষে মোটামুটি অস্বচ্ছ means আমি চাইব না যে আমার সহকর্মীদের মধ্যে কেউ ভবিষ্যতে কেবল এই এক মূর্খ কোডের সংশোধন করার জন্য মনাদ শিখুক। যদিও এটি ভবিষ্যতের রেফারেন্সের জন্য দুর্দান্ত।
jjoelson

1
রসিকতার বোধের জন্য +1, এই সমস্যার পক্ষে সবচেয়ে বেশি অবস্হান এবং ভার্বোজিক সমাধানটি লিখতে এবং তারপরে বলে যে এটি "আপনার কোডের পঠনযোগ্যতা বাড়ানোর সময় আপনাকে যে বয়লারপ্লেট কোডটি লিখতে হবে তা হ্রাস করবে"।
নাট সি কে 21

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

কারণ এটি ব্যবহার Maybeকরে এটি এটিকে একটি মারাত্মক সমাধান করে না; এটি monadic বৈশিষ্ট্যগুলির শূন্য ব্যবহার করে Maybeএবং পাশাপাশি কেবল ব্যবহার করতে পারে null। এছাড়া ব্যবহৃত "monadicly" এই হবে বিপরীত এর Maybe। একটি প্রকৃত monadic সমাধান একটি রাজ্য monad ব্যবহার করা হবে যা প্রথম অ-ব্যতিক্রমী মানকে তার রাজ্য হিসাবে রাখে, তবে যখন সাধারণ বেঁধে দেওয়া মূল্যায়ন কাজ করে তখন ওভারকিল হবে।
ডেক্স ফোহল

7

চেষ্টা পদ্ধতি পদ্ধতির আরেকটি সংস্করণ । প্রতিটি গণনার জন্য একটি ব্যতিক্রম প্রকার রয়েছে বলে এটি একটি টাইপযুক্ত ব্যতিক্রমগুলিকে অনুমতি দেয়:

    public bool Try<T>(Func<double> func, out double d) where T : Exception
    {
      try
      {
        d = func();
        return true;
      }
      catch (T)
      {
        d = 0;
        return false;
      }
    }

    // usage:
    double d;
    if (!Try<Calc1Exception>(() = calc1(), out d) && 
        !Try<Calc2Exception>(() = calc2(), out d) && 
        !Try<Calc3Exception>(() = calc3(), out d))

      throw new NoCalcsWorkedException();
    }

&&পরিবর্তে প্রতিটি শর্তের মধ্যে ব্যবহার করে আপনি নেস্টেড আইএফএস এড়াতে পারবেন।
ডেকাফ

4

পার্ল আপনি কি করতে পারেন foo() or bar(), যা চালানো হবে bar()যদি foo()ব্যর্থ। সি # তে আমরা এটি "ব্যর্থ হলে," তবে এটি নির্মাণ করতে দেখি না, তবে এমন একটি অপারেটর রয়েছে যা আমরা এই উদ্দেশ্যে ব্যবহার করতে পারি: নাল-কোলেস অপারেটর ??, যা কেবল প্রথম অংশটি ফাঁকা থাকলে চলতে থাকে।

আপনি যদি নিজের গণনার স্বাক্ষর পরিবর্তন করতে পারেন এবং যদি আপনি হয় সেগুলির ব্যতিক্রমগুলি (যদিও আগের পোস্টগুলিতে দেখানো হয়) মোড়ানো বা nullতার পরিবর্তে পুনরায় লিখতে পারেন তবে আপনার কোড-চেইন ক্রমশ সংক্ষিপ্ত এবং পড়তে সহজ:

double? val = Calc1() ?? Calc2() ?? Calc3() ?? Calc4();
if(!val.HasValue) 
    throw new NoCalcsWorkedException();

আমি আপনার ফাংশনগুলির জন্য নিম্নলিখিত প্রতিস্থাপনগুলি ব্যবহার করেছি, যার ফলস্বরূপ মান 40.40হয় val

static double? Calc1() { return null; /* failed */}
static double? Calc2() { return null; /* failed */}
static double? Calc3() { return null; /* failed */}
static double? Calc4() { return 40.40; /* success! */}

আমি বুঝতে পারি যে এই সমাধানটি সর্বদা প্রযোজ্য হবে না তবে আপনি একটি খুব আকর্ষণীয় প্রশ্ন উত্থাপন করেছেন এবং আমি বিশ্বাস করি যে থ্রেড তুলনামূলকভাবে পুরানো হলেও আপনি যখন সংশোধন করতে পারেন তখন এটি বিবেচনা করার মতো একটি প্যাটার্ন।


1
আমি শুধু আপনাকে ধন্যবাদ বলতে চাই". আপনি যা বলছেন তা বাস্তবায়নের চেষ্টা করেছি । আমি আশা করি যে আমি এটি সঠিকভাবে বুঝতে পেরেছি।
অ্যালেক্সমেলউ

3

গণনা পদ্ধতিগুলির একই পরামিতিহীন স্বাক্ষর রয়েছে তা প্রদত্ত, আপনি এগুলিকে একটি তালিকায় নিবন্ধন করতে পারেন এবং সেই তালিকাটির মাধ্যমে পুনরাবৃত্তি করতে পারেন এবং পদ্ধতিগুলি কার্যকর করতে পারেন। সম্ভবত আপনার Func<double>অর্থ "এমন কোনও ফাংশন যা টাইপের ফলাফল দেয়" অর্থ ব্যবহার করা আরও ভাল double

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
  class CalculationException : Exception { }
  class Program
  {
    static double Calc1() { throw new CalculationException(); }
    static double Calc2() { throw new CalculationException(); }
    static double Calc3() { return 42.0; }

    static void Main(string[] args)
    {
      var methods = new List<Func<double>> {
        new Func<double>(Calc1),
        new Func<double>(Calc2),
        new Func<double>(Calc3)
    };

    double? result = null;
    foreach (var method in methods)
    {
      try {
        result = method();
        break;
      }
      catch (CalculationException ex) {
        // handle exception
      }
     }
     Console.WriteLine(result.Value);
   }
}

3

আপনি কোনও কার্য / চালিয়ে যাওয়া ব্যবহার করতে পারেন এবং ব্যতিক্রমটি পরীক্ষা করতে পারেন। এটিকে সুন্দর করে তোলার জন্য এখানে একটি দুর্দান্ত এক্সটেনশন পদ্ধতি রয়েছে:

    static void Main() {
        var task = Task<double>.Factory.StartNew(Calc1)
            .OrIfException(Calc2)
            .OrIfException(Calc3)
            .OrIfException(Calc4);
        Console.WriteLine(task.Result); // shows "3" (the first one that passed)
    }

    static double Calc1() {
        throw new InvalidOperationException();
    }

    static double Calc2() {
        throw new InvalidOperationException();
    }

    static double Calc3() {
        return 3;
    }

    static double Calc4() {
        return 4;
    }
}

static class A {
    public static Task<T> OrIfException<T>(this Task<T> task, Func<T> nextOption) {
        return task.ContinueWith(t => t.Exception == null ? t.Result : nextOption(), TaskContinuationOptions.ExecuteSynchronously);
    }
}

1

যদি ফেলে দেওয়া ব্যতিক্রমের প্রকৃত ধরণের কোনও বিষয় না আসে তবে আপনি কেবল একটি টাইপলেস ক্যাচ ব্লক ব্যবহার করতে পারেন:

var setters = new[] { calc1, calc2, calc3 };
bool succeeded = false;
foreach(var s in setters)
{
    try
    {
            val = s();
            succeeded = true;
            break;
    }
    catch { /* continue */ }
}
if (!suceeded) throw new NoCalcsWorkedException();

যে সবসময় তালিকার প্রতিটি ফাংশন কল না? if(succeeded) { break; }পোস্ট-ক্যাচের মতো এমন কিছুতে (পাং উদ্দেশ্য নয়) ছুড়ে দিতে পারে want
একটি সিভিএন

1
using System;

namespace Utility
{
    /// <summary>
    /// A helper class for try-catch-related functionality
    /// </summary>
    public static class TryHelper
    {
        /// <summary>
        /// Runs each function in sequence until one throws no exceptions;
        /// if every provided function fails, the exception thrown by
        /// the final one is left unhandled
        /// </summary>
        public static void TryUntilSuccessful( params Action[] functions )
        {
            Exception exception = null;

            foreach( Action function in functions )
            {
                try
                {
                    function();
                    return;
                }
                catch( Exception e )
                {
                    exception   = e;
                }
            }

            throw exception;
        }
    }
}

এবং এটি এর মতো ব্যবহার করুন:

using Utility;

...

TryHelper.TryUntilSuccessful(
    () =>
    {
        /* some code */
    },
    () =>
    {
        /* more code */
    },
    calc1,
    calc2,
    calc3,
    () =>
    {
        throw NotImplementedException();
    },
    ...
);

1

দেখে মনে হয় যে ওপি'র উদ্দেশ্য ছিল তাঁর সমস্যাটি সমাধান করার জন্য এবং সেই মুহূর্তে তিনি যে বর্তমান সমস্যাটি মোকাবেলা করেছিলেন তার সমাধানের জন্য একটি ভাল প্যাটার্ন খুঁজে বের করা।

ওপি: "আমি প্রতিটি গণনা একটি সহায়ক পদ্ধতিতে গুটিয়ে রাখতে পারি যা ব্যর্থতার পরে বাতিল হয়ে যায়, এবং তারপরে কেবল ??অপারেটরটি ব্যবহার করে , তবে এটি আরও সাধারণভাবে করার উপায় আছে (যেমন আমি চাই প্রতিটি পদ্ধতির জন্য কোনও সহায়ক পদ্ধতি না লিখে) ব্যবহার)? আমি জেনেরিক ব্যবহার করে একটি স্ট্যাটিক পদ্ধতি লেখার কথা ভেবেছি যা কোনও চেষ্টা / ধরাতে যে কোনও পদ্ধতি আবৃত করে এবং ব্যর্থতার পরে বাতিল হয়ে যায়, তবে আমি কীভাবে এটি করব তা নিশ্চিত নই? কোন ধারণা? "

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

ওপিকে উপরে উল্লিখিত হিসাবে, তিনি একটি মোড়কের বস্তু তৈরি করতে চেয়েছিলেন যা nullব্যর্থতায় ফিরে আসে । আমি এটাকে একটি পোড বলব ( ব্যতিক্রম-নিরাপদ পোড )।

public static void Run()
{
    // The general case
    // var safePod1 = SafePod.CreateForValueTypeResult(() => CalcX(5, "abc", obj));
    // var safePod2 = SafePod.CreateForValueTypeResult(() => CalcY("abc", obj));
    // var safePod3 = SafePod.CreateForValueTypeResult(() => CalcZ());

    // If you have parameterless functions/methods, you could simplify it to:
    var safePod1 = SafePod.CreateForValueTypeResult(Calc1);
    var safePod2 = SafePod.CreateForValueTypeResult(Calc2);
    var safePod3 = SafePod.CreateForValueTypeResult(Calc3);

    var w = safePod1() ??
            safePod2() ??
            safePod3() ??
            throw new NoCalcsWorkedException(); // I've tested it on C# 7.2

    Console.Out.WriteLine($"result = {w}"); // w = 2.000001
}

private static double Calc1() => throw new Exception("Intentionally thrown exception");
private static double Calc2() => 2.000001;
private static double Calc3() => 3.000001;

তবে আপনি যদি ক্যালসএন () ফাংশন / পদ্ধতি দ্বারা ফেরত একটি রেফারেন্স টাইপ ফলাফলের জন্য একটি নিরাপদ শুঁটি তৈরি করতে চান ।

public static void Run()
{
    var safePod1 = SafePod.CreateForReferenceTypeResult(Calc1);
    var safePod2 = SafePod.CreateForReferenceTypeResult(Calc2);
    var safePod3 = SafePod.CreateForReferenceTypeResult(Calc3);

    User w = safePod1() ?? safePod2() ?? safePod3();

    if (w == null) throw new NoCalcsWorkedException();

    Console.Out.WriteLine($"The user object is {{{w}}}"); // The user object is {Name: Mike}
}

private static User Calc1() => throw new Exception("Intentionally thrown exception");
private static User Calc2() => new User { Name = "Mike" };
private static User Calc3() => new User { Name = "Alex" };

class User
{
    public string Name { get; set; }
    public override string ToString() => $"{nameof(Name)}: {Name}";
}

সুতরাং, আপনি লক্ষ্য করতে পারেন যে "আপনি যে প্রতিটি পদ্ধতি ব্যবহার করতে চান তার জন্য একটি সহায়ক পদ্ধতি লেখার" দরকার নেই ।

Pods দুই ধরনের (জন্য ValueTypeResults এবং ReferenceTypeResultগুলি) হয় যথেষ্ট


এখানে কোড SafePod। যদিও এটি কোনও ধারক নয়। পরিবর্তে, এটিValueTypeResult গুলি এবং ReferenceTypeResultগুলি উভয়ের জন্য একটি ব্যতিক্রম-নিরাপদ প্রতিনিধি মোড়ক তৈরি করে

public static class SafePod
{
    public static Func<TResult?> CreateForValueTypeResult<TResult>(Func<TResult> jobUnit) where TResult : struct
    {
        Func<TResult?> wrapperFunc = () =>
        {
            try { return jobUnit.Invoke(); } catch { return null; }
        };

        return wrapperFunc;
    }

    public static Func<TResult> CreateForReferenceTypeResult<TResult>(Func<TResult> jobUnit) where TResult : class
    {
        Func<TResult> wrapperFunc = () =>
        {
            try { return jobUnit.Invoke(); } catch { return null; }
        };

        return wrapperFunc;
    }
}

আপনি প্রথম শ্রেণীর নাগরিক সত্তার ( গুলি) এর ??শক্তির সাথে মিলিত নাল-কোলেসিং অপারেটরটি কীভাবে এই উপায়ে নিতে পারেন ।delegate


0

আপনি প্রতিটি গণনা মোড়ানো সম্পর্কে ঠিক বলেছেন তবে আপনাকে বলুন না-জিজ্ঞাসা-নীতি অনুসারে মোড়ানো উচিত।

double calc3WithConvertedException(){
    try { val = calc3(); }
    catch (Calc3Exception e3)
    {
        throw new NoCalcsWorkedException();
    }
}

double calc2DefaultingToCalc3WithConvertedException(){
    try { val = calc2(); }
    catch (Calc2Exception e2)
    {
        //defaulting to simpler method
        return calc3WithConvertedException();
    }
}


double calc1DefaultingToCalc2(){
    try { val = calc2(); }
    catch (Calc1Exception e1)
    {
        //defaulting to simpler method
        return calc2defaultingToCalc3WithConvertedException();
    }
}

ক্রিয়াকলাপগুলি সহজ, এবং স্বাধীনভাবে তাদের আচরণ পরিবর্তন করতে পারে। এবং তারা ডিফল্ট কেন তা বিবেচ্য নয়। প্রমাণ হিসাবে আপনি ক্যালক 1 ডিফল্টিংটোক্যালক 2 বাস্তবায়ন করতে পারেন:

double calc1DefaultingToCalc2(){
    try { 
        val = calc2(); 
        if(specialValue(val)){
            val = calc2DefaultingToCalc3WithConvertedException()
        }
    }
    catch (Calc1Exception e1)
    {
        //defaulting to simpler method
        return calc2defaultingToCalc3WithConvertedException();
    }
}

-1

দেখে মনে হচ্ছে আপনার গণনাগুলিতে কেবল গণনা না করে ফিরে আসার জন্য আরও বৈধ তথ্য রয়েছে। সম্ভবত এটি তাদের নিজস্ব ব্যতিক্রম হ্যান্ডলিং করা এবং ত্রুটি সম্পর্কিত তথ্য, মান তথ্য ইত্যাদি ধারণ করে একটি "ফলাফল" শ্রেণি ফিরিয়ে দেওয়া আরও বোধগম্য হবে Think তারপরে আপনি গণনার আসল ফলাফলটি মূল্যায়ন করতে পারেন। আপনি এই পদে চিন্তা করে এই যুক্তিযুক্ত করতে পারেন যে যদি কোনও গণনা ব্যর্থ হয় তবে এটি ঠিক যেমন তথ্য পাস করে তেমনি। অতএব, ব্যতিক্রম একটি তথ্যের অংশ, "ত্রুটি" নয়।

internal class SomeCalculationResult 
{ 
     internal double? Result { get; private set; } 
     internal Exception Exception { get; private set; }
}

...

SomeCalculationResult calcResult = Calc1();
if (!calcResult.Result.HasValue) calcResult = Calc2();
if (!calcResult.Result.HasValue) calcResult = Calc3();
if (!calcResult.Result.HasValue) throw new NoCalcsWorkedException();

// do work with calcResult.Result.Value

...

অবশ্যই, আমি এই গণনাগুলি সম্পন্ন করতে আপনি যে সামগ্রিক আর্কিটেকচারটি ব্যবহার করছেন তা নিয়ে আরও ভাবছি।


এটি ঠিক আছে - গণনাগুলি মোড়ানো হিসাবে ওপির পরামর্শ মতো। আমি শুধু ভালো কিছু পছন্দ while (!calcResult.HasValue) nextCalcResult(), Calc1, Calc2, Calc3 ইত্যাদি একটি তালিকা পরিবর্তে
কার্ক Broadhurst

-3

আপনার করা ক্রিয়াগুলি ট্র্যাক করার বিষয়ে ...

double val;
string track = string.Empty;

try 
{ 
  track = "Calc1";
  val = calc1(); 

  track = "Calc2";
  val = calc2(); 

  track = "Calc3";
  val = calc3(); 
}
catch (Exception e3)
{
   throw new NoCalcsWorkedException( track );
}

4
কিভাবে এই সাহায্য করে? ক্যালক 1 () ব্যর্থ হলে কল 2 কখনই কার্যকর হবে না!
ডেকাফ

এটি সমস্যার সমাধান করে না। ক্যালক 2 ব্যর্থ হলে কেবল ক্যালক 1 চালান, কেবল 1 ক্যালক 3 কার্যকর করুন যদি ক্যালকা 1 && ক্যালક 2 ব্যর্থ হয়।
জেসন

+1 অর্ণ এই আমি কি কি. আমি শুধুমাত্র কোড আছে এক ধরা, বার্তা (আমার কাছে পাঠানো trackএই ক্ষেত্রে), এবং আমি জানি ঠিক আমার কোড কি সেগমেন্ট সৃষ্ট ব্লক ব্যর্থ। ডেকাফের মতো সদস্যদের বলার জন্য আপনার বিবরণ দেওয়া উচিত ছিল যে trackবার্তাটি আপনার কাস্টম ত্রুটি পরিচালনার রুটিনে প্রেরণ করা হয়েছে যা আপনাকে আপনার কোডটি ডিবাগ করতে সক্ষম করে। মনে হচ্ছে তিনি আপনার যুক্তি বুঝতে পারেন নি।
jp2code

ঠিক আছে, @ ডেকাফটি সঠিক, আমার কোড বিভাগটি পরবর্তী কাজটি চালিয়ে রাখে না যা যাজেলসন বলেছে, সেখানে আমার সমাধানটি কার্যকর নয়
অরন ক্রিস্টজানসন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.