Vă mulțumim pentru susținere

Reducerea codului de eroare duplicat în C #?

Nu am fost niciodată complet mulțumit de modul în care lucrează cu excepția, există multe excepții și încercați / prindeți aduce la masă (stivuirea răsturnării etc.), dar se pare că rupe o mulțime de model OO în acest proces.

Oricum, iată problema:

Să presupunem că aveți o clasă care cuprinde sau include operațiuni IO de fișiere în rețea (de ex. Citirea și scrierea unui fișier pe o anumită cale UNC undeva). Din diverse motive, nu doriți ca aceste operațiuni IO să eșueze, așa că dacă descoperiți că nu reușesc să le reîncercați și le rețineți până când reușesc sau veți ajunge la un timp de expirare. Am deja o clasă RetryTimer convenabilă, pe care o pot instanțiza și o pot folosi pentru a dormi firul curent dintre încercări și pentru a determina când a trecut perioada de expirare etc.

Problema este că aveți o grămadă de operații IO în mai multe metode din această clasă și trebuie să le înfășurați fiecare în logica try-catch / retry.

Iată un exemplu de fragment de cod:

RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10));
bool success = false;
while (!success)
{
    try
    {
        // do some file IO which may succeed or fail
        success = true;
    }
    catch (IOException e)
    {
        if (fileIORetryTimer.HasExceededRetryTimeout)
        {
            throw e;
        }
        fileIORetryTimer.SleepUntilNextRetry();
    }
}

Deci, cum eviți duplicarea celei mai mari părți a acestui cod pentru fiecare operațiune de fișiere IO din întreaga clasă? Soluția mea a fost să folosesc blocuri de delegați anonime și o singură metodă din clasa care a executat blocul de delegați care i-a fost transmisă. Acest lucru mi-a permis să fac astfel de lucruri și în alte metode:

this.RetryFileIO( delegate()
    {
        // some code block
    } );

Îmi place oarecum, dar lasă mult de dorit. Aș dori să aflu cum ar rezolva alte persoane această problemă.

0
adăugat
Doar un general FYI: Este aproape mereu mai bine la throw în loc de throw e;
adăugat autor Dan Tao

4 răspunsuri

Mă întrebam, ce credeți că vă dorește metoda voastră? Ați putea înlocui delegatul anonim cu un nume? delegat, ceva de genul

    public delegate void IoOperation(params string[] parameters);

    public void FileDeleteOperation(params string[] fileName)
    {
        File.Delete(fileName[0]);
    }

    public void FileCopyOperation(params string[] fileNames)
    {
        File.Copy(fileNames[0], fileNames[1]);
    }

    public void RetryFileIO(IoOperation operation, params string[] parameters)
    {
        RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10));
        bool success = false;
        while (!success)
        {
            try
            {
                operation(parameters);
                success = true;
            }
            catch (IOException e)
            {
                if (fileIORetryTimer.HasExceededRetryTimeout)
                {
                    throw;
                }
                fileIORetryTimer.SleepUntilNextRetry();
            }
        }
    }

    public void Foo()
    {
        this.RetryFileIO(FileDeleteOperation, "L:\file.to.delete" );
        this.RetryFileIO(FileCopyOperation, "L:\file.to.copy.source", "L:\file.to.copy.destination" );
    }
0
adăugat

Aceasta arata ca o ocazie excelenta de a arunca o privire la programarea orientata pe aspect. Iată un bun articol despre AOP în .NET . Ideea generală este să extrageți preocuparea inter-funcțională (adică Reîncercați pentru x ore) într-o clasă separată și apoi să adnotați metode care necesită modificarea comportamentului în acest fel. Iată cum ar putea să arate (cu o metodă de extensie frumoasă pe Int32)

[RetryFor( 10.Hours() )]
public void DeleteArchive()
{
  //.. code to just delete the archive
}
0
adăugat

Puteți utiliza, de asemenea, o abordare OO mai mult:

  • Creați o clasă de bază care gestionează erorile și solicită o metodă abstractă pentru a efectua lucrările concrete. (Model de metodă de șablon)
  • Creați clase concrete pentru fiecare operație.

Acest lucru are avantajul de a numi fiecare tip de operație pe care o efectuați și vă oferă un model de comandă - operațiile au fost reprezentate ca obiecte.

0
adăugat

Iată ce am făcut recent. Probabil că a fost făcută în altă parte mai bine, dar pare destul de curată și reutilizabilă.

Am o metodă utilitară care arată astfel:

    public delegate void WorkMethod();

    static public void DoAndRetry(WorkMethod wm, int maxRetries)
    {
        int curRetries = 0;
        do
        {
            try
            {
                wm.Invoke();
                return;
            }
            catch (Exception e)
            {
                curRetries++;
                if (curRetries > maxRetries)
                {
                    throw new Exception("Maximum retries reached", e);
                }
            }
        } while (true);
    }

Apoi, în cererea mea, folosesc sintaxa expresiei Lamda c # pentru a păstra lucrurile ordonate:

Utility.DoAndRetry( () => ie.GoTo(url), 5);

Acest lucru îmi numește metoda și mă reia de până la 5 ori. În cea de-a cincea încercare, excepția inițială este revocată în interiorul unei excepții de retur.

0
adăugat
Dar de ce delegatul WorkMethod personalizat în loc de Action ?
adăugat autor Dan Tao