Oltre al codice generato dal designer, le classi parziali possono ancora essere utili senza compromettere la qualità del codice.
Esempi di uso corretto
Alcuni utilizzi che ho trovato particolarmente utili sono i seguenti:
-
query SQL
Faccio un sacco di cose in cui vengono usate query SQL dirette e scritte a mano invece di ORM. L'architettura più semplice è incorporare le query direttamente nel codice:
public class Demo
{
[...]
public int GetPriceOfProduct(long productId)
{
var query = Query(
"select top 1 [Price] from [Shop].[Products] where [ProductId] = @ProductId",
this.ConnectionString);
// Use the query to retrieve the price.
}
[...]
}
Funziona bene, finché non ci sono troppi metodi e fino a quando troppe query iniziano ad avere più di dieci righe. Anche la revisione delle query da parte del DBA è complicata, dal momento che il DBA deve cercare attraverso tutto il codice. In questo caso, le query possono essere inserite in una classe separata:
public class Demo
{
[...]
public int GetPriceOfProduct(long productId)
{
var query = Query(Queries.PriceOfProduct, this.ConnectionString);
// Use the query to retrieve the price.
}
[...]
private static class Queries
{
public const string PriceOfProduct =
@"select top 1 [Price] from [Shop].[Products] where [ProductId] = @ProductId";
}
}
Una volta terminato, la sottoclasse Queries
è un buon candidato da inserire in un file separato.
Un modo è renderlo internal
, rinominare DemoQueries
e creare un file DemoQueries.cs
. Il problema è che il codice sarà più lungo (specificare ProductOfUserRelationQueries
invece di solo Queries
è in realtà un po 'più lungo) e che la classe inquinerà l'assembly (incluso Intellisense), mentre è necessario accedervi solo in Demo
class.
Un altro modo è di rendere Demo
partial e di avere le query in un file separato, mantenendo la classe Queries
in Demo
.
-
Nomi nella cache
Quando metti in cache alcuni elementi, è importante essere coerenti con i nomi (chiavi) usati nella cache. Per questo e simile al primo esempio, si può finire con una classe privata contenente un sacco di metodi come questo:
public class Demo
{
[...]
private static class CacheNames
{
public static string PriceOfProduct(long productId)
{
return string.Format(@"Price(ProductId<{0}>)", productId);
}
}
[...]
}
-
traccia
Lo stesso vale per la traccia. A volte, un codice che si basa molto sulla traccia è quasi illeggibile dal momento che gli ID ei messaggi di traccia sprecano troppo spazio. Mettere una this.Trace(TraceId.UserRemoved)
rende il codice molto più breve, ma devi definire tutti i TraceId
s e i relativi ID evento e messaggi da qualche parte.
-
sovraccarichi
Questo caso è piuttosto strano e deve essere evitato nella maggior parte dei casi, ma comunque ci sono esempi validi in cui le classi parziali aiutano.
Ho avuto un progetto in cui i metodi avevano un'enorme quantità di overload e altri metodi che non stavano facendo nulla di utile piuttosto che chiamare un metodo diverso cambiando alcuni parametri. Erano richiesti perché erano utilizzati dai consumatori della biblioteca, ma era davvero fastidioso vederli tutti nel codice sorgente e essere distratti dal codice reale (notare che anche un sovraccarico può essere piuttosto lungo in termini di LOC: a almeno tre righe per XMLDoc, ma a volte erano più di venti righe, poi il metodo stesso, con i suoi contratti di codice e poi una piccola chiamata allo stesso metodo con parametri diversi).
Mettendo in un file separato tutti i sovraccarichi e i metodi che non stavano facendo troppo aiutarono a concentrarsi sul codice base.
Le classi parziali sono le stesse delle regioni
#region
s non è buona , e uno dei motivi per cui sono così cattivi è che danno hai l'impressione che il tuo codice sia piccolo, quando in realtà ha migliaia di righe ed è completamente illeggibile. Il problema principale è quando qualcuno li usa all'interno di un metodo, per finire con centinaia di linee di codice in questo metodo che ora fa venti cose invece di una.
Le classi parziali hanno lo stesso problema: se ti affidi troppo a loro, puoi avere l'impressione che la tua classe sia piuttosto piccola, mentre dovrebbe essere stata sottoposta a refactoring per mesi.
Potrebbero rendere difficile la navigazione del codice
Un altro difetto con le classi parziali è che non è così facile sapere dove sono le cose. Prendi Initialize()
in Windows Form. Per una persona che scopre Windows Form e classi parziali, non è così facile capire dove si trova questo metodo: è un metodo della classe genitore Form
e fa parte di .NET Framework? O forse è da qualche parte nel codice per MyForm
? Oh, no, è in un codice generato dal designer.
Sì, Visual Studio ha la funzione F12 - Vai alla definizione, ma se puoi organizzare il tuo codice in file in modo che la persona possa sapere dove si trova un metodo, fallo.