Ajánlott eljárások a kivételekhez
A megfelelő kivételkezelés elengedhetetlen az alkalmazások megbízhatóságához. Szándékosan kezelheti a várt kivételeket, hogy megakadályozza az alkalmazás összeomlását. Az összeomlott alkalmazások azonban megbízhatóbbak és kiszámíthatóbbak, mint a meghatározatlan viselkedésű alkalmazások.
Ez a cikk a kivételek kezelésének és létrehozásának ajánlott eljárásait ismerteti.
Kivételek kezelése
A következő ajánlott eljárások a kivételek kezelésére vonatkoznak:
- Try/catch/finally blocks használata a hibákból való helyreállításhoz vagy az erőforrások felszabadításához
- Gyakori feltételek kezelése a kivételek elkerülése érdekében
- A lemondás és az aszinkron kivételek fogása
- Osztályokat tervezhet, hogy elkerülhetők legyenek a kivételek
- Állapot visszaállítása, ha a metódusok kivétel miatt nem fejeződnek be
- Kivételek rögzítése és újbóli visszaállítása
Try/catch/finally blocks használata a hibákból való helyreállításhoz vagy az erőforrások felszabadításához
Olyan kód esetén, amely potenciálisan létrehozhat egy kivételt, és amikor az alkalmazás helyre tud állni ebből a kivételből, használjon try
/catch
blokkokat a kód körül. Blokkokban catch
mindig rendezze a kivételeket a legelvezetettebbtől a legkevésbé származtatottig. (Minden kivétel az Exception osztályból származik. A származtatottabb kivételeket nem egy catch
olyan záradék kezeli, amelyet egy catch
alapkivételi osztály záradéka előz meg.) Ha a kód nem tud helyreállítani egy kivételt, ne kapja meg ezt a kivételt. Ha lehetséges, engedélyezze a metódusok további használatát a hívásverem helyreállításához.
Távolítsa el az utasításokkal vagy finally
blokkokkal using
lefoglalt erőforrásokat. A kivételekkel automatikusan törölheti az erőforrásokat az utasítások előnyben using
részesítéséhez. Blokkok használatával finally
törölheti a nem implementálható IDisposableerőforrásokat. A záradékban lévő finally
kód szinte mindig végrehajtásra kerül, még akkor is, ha kivételeket adnak ki.
Gyakori feltételek kezelése a kivételek elkerülése érdekében
Olyan feltételek esetén, amelyek valószínűleg előfordulnak, de kivételt válthatnak ki, fontolja meg a kivétel elkerülését szolgáló kezelésüket. Ha például egy már lezárt kapcsolatot próbál bezárni, kap egy InvalidOperationException
. Ezt elkerülheti, ha egy if
utasítással ellenőrzi a kapcsolat állapotát, mielőtt megpróbálná bezárni.
if (conn.State != ConnectionState.Closed)
{
conn.Close();
}
If conn.State <> ConnectionState.Closed Then
conn.Close()
End IF
Ha a lezárás előtt nem ellenőrzi a kapcsolat állapotát, elkaphatja a kivételt InvalidOperationException
.
try
{
conn.Close();
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.GetType().FullName);
Console.WriteLine(ex.Message);
}
Try
conn.Close()
Catch ex As InvalidOperationException
Console.WriteLine(ex.GetType().FullName)
Console.WriteLine(ex.Message)
End Try
A választás módszere attól függ, hogy milyen gyakran várható az esemény.
Használjon kivételkezelést, ha az esemény nem gyakran fordul elő, vagyis ha az esemény valóban kivételes, és hibát jelez, például egy váratlan fájlvégzeményt. Kivételkezelés használatakor a rendszer normál körülmények között kevesebb kódot hajt végre.
Ellenőrizze, hogy van-e hibaállapot a kódban, ha az esemény rutinszerűen történik, és a normál végrehajtás része lehet. A gyakori hibafeltételek ellenőrzésekor kevesebb kód lesz végrehajtva, mert elkerüli a kivételeket.
Feljegyzés
Az előzetes ellenőrzések az esetek többségében kiküszöbölik a kivételeket. Lehetnek azonban versenyfeltételek, amikor az őrzött állapot megváltozik az ellenőrzés és a művelet között, és ebben az esetben továbbra is kivételt okozhat.
Metódusok meghívása Try*
a kivételek elkerülése érdekében
Ha a kivételek teljesítményköltsége tiltott, egyes .NET-kódtár-metódusok alternatív hibakezelési formákat biztosítanak. Ha például az elemezni kívánt érték túl nagy ahhoz, Int32.ParseOverflowException hogy a Int32függvényt képviselje. Ezt a Int32.TryParse kivételt azonban nem veti ki. Ehelyett egy logikai értéket ad vissza, és egy out
paraméterrel rendelkezik, amely az elemezt érvényes egész számot tartalmazza a sikerhez. Dictionary<TKey,TValue>.TryGetValue hasonló viselkedéssel rendelkezik egy érték szótárból való lekéréséhez.
A lemondás és az aszinkron kivételek fogása
Jobb, ha aszinkron OperationCanceledException metódust hív meg, ahelyett TaskCanceledException, hogy OperationCanceledException
aszinkron metódust hív. Számos aszinkron metódus kivételt OperationCanceledException jelez, ha lemondást kérnek. Ezek a kivételek lehetővé teszik a végrehajtás hatékony leállítását, és a hívásokat a lemondási kérések figyelése után nem lehet feloldani.
Az aszinkron metódusok olyan kivételeket tárolnak, amelyeket a visszaadott feladat végrehajtása során dobnak ki. Ha egy kivételt tárol a visszaadott tevékenységben, akkor a rendszer akkor küldi el a kivételt, amikor a feladatra vár. A használati kivételeket( például ArgumentException) továbbra is szinkron módon dobják ki. További információ: Aszinkron kivételek.
Osztályokat tervezhet, hogy elkerülhetők legyenek a kivételek
Az osztály olyan metódusokat vagy tulajdonságokat biztosíthat, amelyekkel elkerülheti a kivételt kiváltó hívásokat. Az osztály például olyan metódusokat biztosít, FileStream amelyek segítenek megállapítani, hogy a fájl vége el lett-e érve. Ezeket a metódusokat meghívva elkerülheti a kivételt, amely akkor fordul elő, ha a fájl végén olvas. Az alábbi példa bemutatja, hogyan olvasható a fájl végéig kivétel aktiválása nélkül:
class FileRead
{
public static void ReadAll(FileStream fileToRead)
{
ArgumentNullException.ThrowIfNull(fileToRead);
int b;
// Set the stream position to the beginning of the file.
fileToRead.Seek(0, SeekOrigin.Begin);
// Read each byte to the end of the file.
for (int i = 0; i < fileToRead.Length; i++)
{
b = fileToRead.ReadByte();
Console.Write(b.ToString());
// Or do something else with the byte.
}
}
}
Class FileRead
Public Sub ReadAll(fileToRead As FileStream)
' This if statement is optional
' as it is very unlikely that
' the stream would ever be null.
If fileToRead Is Nothing Then
Throw New System.ArgumentNullException()
End If
Dim b As Integer
' Set the stream position to the beginning of the file.
fileToRead.Seek(0, SeekOrigin.Begin)
' Read each byte to the end of the file.
For i As Integer = 0 To fileToRead.Length - 1
b = fileToRead.ReadByte()
Console.Write(b.ToString())
' Or do something else with the byte.
Next i
End Sub
End Class
A kivételek elkerülésének másik módja, ha a legtöbb gyakori hibaesetnél a kivétel kiírása helyett az alapértelmezett értéket adja vissza null
. Egy gyakori hibaeset normál vezérlési folyamatnak tekinthető. Ha ezekben az esetekben visszaadja null
(vagy alapértelmezés szerint) a teljesítményre gyakorolt hatást, minimalizálja az alkalmazásra gyakorolt hatást.
Értéktípusok esetén fontolja meg, hogy az alkalmazáshoz használandó-e Nullable<T>
vagy default
hibajelző. A használatával Nullable<Guid>
ahelyett , hogy Guid.Empty
null
a . default
A hozzáadás Nullable<T>
néha egyértelműbbé teheti, ha egy érték jelen van vagy hiányzik. Más esetekben a hozzáadás Nullable<T>
további eseteket is létrehozhat, amelyek nem szükségesek, és csak a lehetséges hibaforrások létrehozására szolgálnak.
Állapot visszaállítása, ha a metódusok kivétel miatt nem fejeződnek be
A hívóknak fel kell tudniuk venni, hogy nincsenek mellékhatások, ha kivételt okoznak egy metódusból. Ha például olyan kóddal rendelkezik, amely az egyik fiókból való kivonással és egy másik fiókban való befizetéssel utalja át a pénzt, és a befizetés végrehajtása során kivétel történik, nem szeretné, hogy a visszavonás érvényben maradjon.
public void TransferFunds(Account from, Account to, decimal amount)
{
from.Withdrawal(amount);
// If the deposit fails, the withdrawal shouldn't remain in effect.
to.Deposit(amount);
}
Public Sub TransferFunds(from As Account, [to] As Account, amount As Decimal)
from.Withdrawal(amount)
' If the deposit fails, the withdrawal shouldn't remain in effect.
[to].Deposit(amount)
End Sub
Az előző metódus nem ad közvetlenül kivételeket. A metódust azonban meg kell írnia, hogy a betéti művelet meghiúsulása esetén a visszavonás vissza legyen állítva.
Ennek a helyzetnek az egyik módja az, hogy elkapja a betéti tranzakció által előidézett kivételeket, és visszaállítja a visszavonást.
private static void TransferFunds(Account from, Account to, decimal amount)
{
string withdrawalTrxID = from.Withdrawal(amount);
try
{
to.Deposit(amount);
}
catch
{
from.RollbackTransaction(withdrawalTrxID);
throw;
}
}
Private Shared Sub TransferFunds(from As Account, [to] As Account, amount As Decimal)
Dim withdrawalTrxID As String = from.Withdrawal(amount)
Try
[to].Deposit(amount)
Catch
from.RollbackTransaction(withdrawalTrxID)
Throw
End Try
End Sub
Ez a példa bemutatja az eredeti kivétel újbóli hitelesítésének throw
használatát, így a hívók könnyebben láthatják a probléma valódi okát anélkül, hogy meg kellene vizsgálniuk a tulajdonságot InnerException . Másik lehetőségként új kivételt kell megadni, és az eredeti kivételt belső kivételként kell megadni.
catch (Exception ex)
{
from.RollbackTransaction(withdrawalTrxID);
throw new TransferFundsException("Withdrawal failed.", innerException: ex)
{
From = from,
To = to,
Amount = amount
};
}
Catch ex As Exception
from.RollbackTransaction(withdrawalTrxID)
Throw New TransferFundsException("Withdrawal failed.", innerException:=ex) With
{
.From = from,
.[To] = [to],
.Amount = amount
}
End Try
Kivételek rögzítése és újbóli visszaállítása
A kivétel kivédése után az általa hordozott információk egy része a verem nyomkövetése. A veremkövetés a metódushívási hierarchia azon hierarchiájának listája, amely azzal a metódussal kezdődik, amely a kivételt eldobja, és a kivételt elkapó metódussal végződik. Ha például az utasításban throw
szereplő kivétel megadásával újrakezd egy kivételt, a verem nyomkövetése újraindul az aktuális metódusban, throw e
és a metódushívások listája az eredeti metódus között, amely a kivételt eldobta, és az aktuális metódus elveszik. Az eredeti verem nyomkövetési adatainak kivétellel való megőrzéséhez két lehetőség közül választhat, amelyek attól függenek, hogy honnan szeretné újból létrehozni a kivételt:
- Ha a kivételt a kivételpéldányt észlelt kezelőből (
catch
blokkból) újból meg kell adnia, használja azthrow
utasítást a kivétel megadása nélkül. A CA2200 kódelemzési szabály segít megtalálni a kód azon helyeit, ahol véletlenül elveszítheti a verem nyomkövetési adatait. - Ha a kivételt a kezelőtől (
catch
blokktól) eltérő helyről szeretné újból létrehozni, akkor a ExceptionDispatchInfo.Capture(Exception) kivétel rögzítéséhez használja a kezelőben, és ExceptionDispatchInfo.Throw() amikor újra meg szeretné oldani. A tulajdonság használatával ExceptionDispatchInfo.SourceException megvizsgálhatja a rögzített kivételt.
Az alábbi példa bemutatja, hogyan használható az ExceptionDispatchInfo osztály, és hogyan nézhet ki a kimenet.
ExceptionDispatchInfo? edi = null;
try
{
var txt = File.ReadAllText(@"C:\temp\file.txt");
}
catch (FileNotFoundException e)
{
edi = ExceptionDispatchInfo.Capture(e);
}
// ...
Console.WriteLine("I was here.");
if (edi is not null)
edi.Throw();
Ha a példakódban szereplő fájl nem létezik, a következő kimenet jön létre:
I was here.
Unhandled exception. System.IO.FileNotFoundException: Could not find file 'C:\temp\file.txt'.
File name: 'C:\temp\file.txt'
at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options)
at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.StreamReader.ValidateArgsAndOpenPath(String path, Encoding encoding, Int32 bufferSize)
at System.IO.File.ReadAllText(String path, Encoding encoding)
at Example.ProcessFile.Main() in C:\repos\ConsoleApp1\Program.cs:line 12
--- End of stack trace from previous location ---
at Example.ProcessFile.Main() in C:\repos\ConsoleApp1\Program.cs:line 24
Kivételek kivetése
Az alábbi ajánlott eljárások a kivételek kizárásával kapcsolatosak:
- Előre definiált kivételtípusok használata
- Kivételszerkesztő metódusok használata
- Honosított sztringüzenet belefoglalása
- A megfelelő nyelvhelyesség használata
- Dobás utasítások jól
- Ne emelje ki a kivételeket a végső záradékokban
- Ne emelje ki a váratlan helyek kivételeit
- Argumentumérvényesítési kivételek szinkronizálása
Előre definiált kivételtípusok használata
Csak akkor vezessen be új kivételosztályt, ha egy előre definiált osztály nem érvényes. Példa:
- Ha egy tulajdonságkészlet vagy metódushívás nem megfelelő az objektum aktuális állapota miatt, kivételt InvalidOperationException kell kivenni.
- Ha érvénytelen paramétereket ad át, adjon meg egy kivételt ArgumentException vagy egy előre definiált osztályt, amely a következőből ArgumentExceptionszármazik: .
Feljegyzés
Bár a legjobb, ha lehetőség szerint előre definiált kivételtípusokat használ, bizonyos fenntartott kivételtípusokat nem érdemes létrehozni, példáulAccessViolationException: , IndexOutOfRangeExceptionNullReferenceException ésStackOverflowException. További információ: CA2201: Ne emeljen fenntartott kivételtípusokat.
Kivételszerkesztő metódusok használata
Gyakran előfordul, hogy egy osztály ugyanazt a kivételt dobja ki a megvalósítás különböző pontjairól. A túlzott kód elkerülése érdekében hozzon létre egy segédmetódust, amely létrehozza a kivételt, és visszaadja azt. Példa:
class FileReader
{
private readonly string _fileName;
public FileReader(string path)
{
_fileName = path;
}
public byte[] Read(int bytes)
{
byte[] results = FileUtils.ReadFromFile(_fileName, bytes) ?? throw NewFileIOException();
return results;
}
static FileReaderException NewFileIOException()
{
string description = "My NewFileIOException Description";
return new FileReaderException(description);
}
}
Class FileReader
Private fileName As String
Public Sub New(path As String)
fileName = path
End Sub
Public Function Read(bytes As Integer) As Byte()
Dim results() As Byte = FileUtils.ReadFromFile(fileName, bytes)
If results Is Nothing
Throw NewFileIOException()
End If
Return results
End Function
Function NewFileIOException() As FileReaderException
Dim description As String = "My NewFileIOException Description"
Return New FileReaderException(description)
End Function
End Class
Néhány fontos .NET-kivételtípus rendelkezik olyan statikus throw
segédmeteként, amely lefoglalja és elveti a kivételt. A megfelelő kivételtípus létrehozása és dobása helyett az alábbi metódusokat kell meghívnia:
- ArgumentNullException.ThrowIfNull
- ArgumentException.ThrowIfNullOrEmpty(String, String)
- ArgumentException.ThrowIfNullOrWhiteSpace(String, String)
- ArgumentOutOfRangeException.ThrowIfZero<T>(T, String)
- ArgumentOutOfRangeException.ThrowIfNegative<T>(T, String)
- ArgumentOutOfRangeException.ThrowIfEqual<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfLessThan<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfNotEqual<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfNegativeOrZero<T>(T, String)
- ArgumentOutOfRangeException.ThrowIfGreaterThan<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfLessThanOrEqual<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual<T>(T, T, String)
- ObjectDisposedException.ThrowIf
Tipp.
Az alábbi kódelemzési szabályok segítenek megtalálni a kódban azokat a helyeket, ahol kihasználhatja ezeket a statikus throw
segítőket: CA1510, CA1511, CA1512 és CA1513.
Ha aszinkron metódust implementál, ahelyett, hogy CancellationToken.ThrowIfCancellationRequested() ellenőriznie kell, hogy a törlést kérték-e, majd hozzon létre és dobjon OperationCanceledException. További információ: CA2250.
Honosított sztringüzenet belefoglalása
A felhasználó által látott hibaüzenet a Exception.Message kidobott kivétel tulajdonságából származik, nem pedig a kivételosztály nevéből. Általában úgy rendel hozzá értéket a Exception.Message tulajdonsághoz, hogy átadja az üzenetsztringet egy message
kivételkonstruktor argumentumának.
Honosított alkalmazások esetén minden olyan kivételhez meg kell adnia egy honosított üzenetsztringet, amelyet az alkalmazás ki tud dobni. Az erőforrásfájlok segítségével honosított hibaüzeneteket jeleníthet meg. Az alkalmazások honosításával és a honosított sztringek lekérésével kapcsolatos információkért tekintse meg az alábbi cikkeket:
- Útmutató: Felhasználó által definiált kivételek létrehozása honosított kivételüzenetekkel
- Erőforrások a .NET-alkalmazásokban
- System.Resources.ResourceManager
A megfelelő nyelvhelyesség használata
Írjon egyértelmű mondatokat, és adja meg a befejezési írásjeleket. A tulajdonsághoz Exception.Message rendelt sztring minden mondatának ponttal kell végződnie. Például a "A naplótábla túlcsordult". Helyes nyelvtant és írásjeleket használ.
Dobás utasítások jól
Helyezze el a dobási utasításokat, ahol a verem nyomkövetése hasznos lesz. A verem nyomkövetése azon az utasításon kezdődik, ahol a kivétel ki lesz dobva, és a catch
kivételt elkapó utasításnál ér véget.
Ne emelje ki a kivételeket a végső záradékokban
Ne emeljen kivételeket a finally
záradékokban. További információ: CA2219 kódelemzési szabály.
Ne emelje ki a váratlan helyek kivételeit
Egyes metódusoknak, például Equals
a , GetHashCode
és ToString
metódusoknak, statikus konstruktoroknak és egyenlőségi operátoroknak nem szabad kivételeket kivenni. További információ: CA1065 kódelemzési szabály.
Argumentumérvényesítési kivételek szinkronizálása
A feladat-visszaadó metódusokban ellenőriznie kell az argumentumokat, és ki kell dobnia a megfelelő kivételeket, például ArgumentException és ArgumentNullException, mielőtt beírja a metódus aszinkron részét. A metódus aszinkron részében megjelenő kivételek a visszaadott tevékenységben vannak tárolva, és csak akkor jelennek meg, ha például a feladatra vár. További információ: Kivételek a feladatvisszaküldött metódusokban.
Egyéni kivételtípusok
Az alábbi ajánlott eljárások az egyéni kivételtípusokra vonatkoznak:
- Kivételosztálynevek vége a következővel:
Exception
- Három konstruktor felvétele
- Szükség szerint adjon meg további tulajdonságokat
Kivételosztálynevek vége a következővel: Exception
Ha egyéni kivételre van szükség, nevezze el megfelelően, és származtassa az Exception osztályból. Példa:
public class MyFileNotFoundException : Exception
{
}
Public Class MyFileNotFoundException
Inherits Exception
End Class
Három konstruktor felvétele
A saját kivételosztályok létrehozásakor legalább a három gyakori konstruktort használja: a paraméter nélküli konstruktort, egy sztringüzenetet tartalmazó konstruktort, valamint egy sztringüzenetet és egy belső kivételt tartalmazó konstruktort.
- Exception(), amely alapértelmezett értékeket használ.
- Exception(String), amely egy sztringüzenetet fogad el.
- Exception(String, Exception), amely egy sztringüzenetet és egy belső kivételt fogad el.
Példa: Útmutató: Felhasználó által definiált kivételek létrehozása.
Szükség szerint adjon meg további tulajdonságokat
Csak akkor adjon meg további tulajdonságokat egy kivételhez (az egyéni üzenetsztring mellett), ha olyan programozott forgatókönyv van, amelyben a további információk hasznosak. Például a FileNotFoundException tulajdonságot adja FileName meg.
Lásd még
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: