3.20.2009

C# drobky: extension methods - IsNull(), IsNotNull(), ForNotNull(), AsString(), Nop()

Rozhodl jsem se, že některé své kusy kódu, které jsou dostatečně malé a jednoduché budu publikovat na blogu, aby je mohl používat taky někdo jiný. V zásadě se nejdná o nic světoborného - každý by to dokázal napsat sám - ale někomu to snad ulehčí práci nebo inspiruje.

Dnes tu mám pár extension metod, které poměrně často používám. Všechny - až na poslední - mají co do činění s NULL odkazem.
První dvě jsou tyto:
public static partial class ObjectExtensionHelper {
public static bool IsNull( this object obj ) {
return object.ReferenceEquals( obj, null );
}

public static bool IsNull( this object obj ) {
return object.ReferenceEquals( obj, null );
}
}

Leckoho napadne k čemu je něco takového dobré, když tu existuje jednoduchý zápis:
variable == null

Vysvětlení je jednoduché. Už se mi stalo, že jsem použil třídu třetí strany, kde někoho napadla "skvélá" myšlenka - přepsat operátor "==" a při porovnání s null vracet false. Pro vyvarování se tomuto případu je potřeba používat statickou metodu "object.ReferenceEquals", jenže její použití je při kombinaci s četností kontroly na NULL pro mě příliš zdlouhavá. Tímto způsobem jsem toho všeho ušetřen.

Další metodu, kterou tu mám je podobného rázu.
public static partial class ObjectExtensionHelper {
public static bool ForNotNull( this T obj, Action notNullAction ) {
var ret = obj.IsNotNull();
if ( ret ) {
notNullAction( obj );
}

return ret;
}
}

A k čemu že to je dobré? Občas se stává, že se musím probrat hierarchií několika objektů, než se dostanu ke kýženému cíli.

Například:
var address = envelope.Invoice.Header.BuyersParty.Party.Address;

To není nic tak strašného, pokud není potřeba každý objekt kontrolovat na null. Takový záspis pak není ani stručný, ani moc přehledný:
if ( envelope.IsNotNull() ) {
Address address = null;
var invoice = envelope.Invoice;
if ( invoice.IsNotNull() ) {
var header = invoice.Header;
if ( header.IsNotNull() ) {
var buyersParty = header.BuyersParty;
if ( buyersParty.IsNotNull() ) {
var party = buyersParty.Party;
if ( party.IsNotNull() ) {
address = party.Address;
}
}
}
}
}

nicméně pomocí metody "ForNotNull()" a lambda výrazům to celé jde celé alespoň trochu zkrátit:
Address address = null;
envelope.ForNotNull(
e => e.Invoice.ForNotNull(
i => i.Header.ForNotNull(
h => h.BuyersParty.ForNotNull(
b => b.Party.ForNotNull(
p => address = p.Address )
)
)
)
);


Další z této sady metod je "AsString()". Jestliže někde často používáte volání "ToString()" a vždy předtím kontrolujete zda cíl volání není null, neobejdete se bez ifu či operátoru "?". Tato metoda to má ulehčit:
public static partial class ObjectExtensionHelper {
public static string AsString( this object obj ) {
if ( obj.IsNull() ) { return obj.ToString(); }
return null;
}


Na závěr tu mám jednu metodu, která se od těch předchozích liší. Ona se vlastně liší téměř ode všech metod. Má totiž za úkol nedělat vůbec nic.
public static partial class ObjectExtensionHelper {
[System.Diagnostics.Conditional( "DEBUG" )]
public static void Nop( this object obj ) { /* nedela nic */ }
}


A k čemu to je celé dobré? Slouží mi právě při zmíněném ladéní programu. Občas není v metodě žádné vhodné místo, kam bych umístil breakpoint. Vložím tedy to kódu volání "Nop()" kam potřebuji, a aniž bych změnil funkcionalitu metody, mám volné místo pro breakpoint.

Atributem "[Conditional]" se určuje při jakých přepínačích má kompilátor zahrnout volání označené metody. Jestliže budete volat tuto metodu a zkompilujete zdrojové kódy s přepínačem "DEBUG" (což je téměř vždy, když chcete program ladit), tak její volání zahrne do kompilace. V ostatních případech (například při kompilaci pro zákazníka) její volání nezkompiluje.

Toť je pro dnešek vše.