C# jak ho možná ještě neznáte – 3. díl

Ondřej Janáček       26.08.2014       C#, .NET       13331 zobrazení

Pokračuji v odhalování dalších “skrytých” vlastností jazyka. Předchozí díl najdete zde.

Duck Typing v .NET

Tato vlastnost známá především v prostředí dynamických jazyků si našla cestu i do .NET Frameworku. Vím zatím o dvou případech.

1) Máte vlastní typ reprezentující kolekci dat? Pak můžete jeho instanci vytvořit použitím inicializátoru kolekcí, který vyžaduje, aby daný typ implementoval rozhraní IEnumerable nebo jeho generickou verzi, což je pro kolekce vcelku běžné. Kromě toho už jen stačí mít veřejnou instanční metodu Add, přijímající libovolný počet parametrů libovolného typu (podle toho se také pak inicializátor používá).

class MyCollection : IEnumerable<string>
{
    private List<string> data = new List<string>();

    public void Add(string text)
    {
        data.Add(text);
    }

    public IEnumerator<string> GetEnumerator()
    {
        return data.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

// použití
var myCol = new MyCollection { "foo", "bar" };

Pro více parametrů (použití např. s Dictionary pro 2 parametry)

public void Add(string key, int value, bool overwrite = true)
{
    if (data.ContainsKey(key)
    {
        if (!overwrite)
            throw new ArgumentException();
        else
            data[key] = value;
    }    
    else
        data.Add(key, value);
}

// následující sice moc nedává smysl, ale ukazuje použití více parametrů
// metoda Add je samozřejmě použitelná nejen v tomto scénáři
var myCol = new MyCollection { { "foo", 1 }, { "foo", 2, false } };

2) Projetí kolekce pomocí foreach nevyžaduje, aby kolekce implementovala rozhraní IEnumerable nebo jeho generickou verzi. Stačí, když má veřejnou metodu GetEnumerator, která vrací typ X implementující IEnumerator nebo jeho generickou verzi. Ale vlastně ani to není podmínkou. Postačí, když tento typ X vystavuje veřejnou metodu bool MoveNext() a property Current libovolného typu (záleží na Vaší implementaci). Toto v .NET Frameworku existuje již od verze 1.0 a bylo přidáno proto, aby bylo možné procházet kolekce hodnotového typu bez boxingu.

Method Group

Možná jste se někdy dostali do následující situace

Console.WriteLine(10.ToString);

kdy zapomenuté závorky vyvolají poměrně kryptickou chybu při kompilaci: “Argument 1: cannot convert from 'method group' to 'bool'”. Nebo Vás na spojení “method group” přivedl ReSharper v rámci svých tipů a triků? Na tom moc nezáleží. Důležitá otázka je, co je to “method group”? Zde nám poradí například C# specifikace (§7.1 v C# 5.0 specs).

Method group je jméno pro skupinu metod, která je výsledkem tzv. “member lookup”, což je operace vyhledávající všechny kompatibilní členy pro nějakou akci (naše akce je Console.WriteLine). Tedy v našem případě je výsledkem této operace jméno ToString, které reprezentuje všechna nalezená přetížení metody ToString pro číslo 10. Mohou to být jak instanční metody, tak extension metody. Výsledkem této operace může být také jediný vhodný kandidát. Ze skupiny je takový vybrán na základě složitých pravidel, která jsem mírně nakousnul v článku Method Overloading.

Už tedy víme, co je to method group, ale stále nevíme, proč ji nejde podle chyby zkonvertovat třeba na bool. Zde pomůže sekce §6.6 Method group conversions, která říká, že method group lze zkonvertovat pouze na delegát odpovídajícího typu. Pravidla této konverze jsou tam také uvedena včetně následujícího příkladu. Pro více informací Vás proto odkazuji tam.

delegate string D1(object o);
delegate object D2(string s);
delegate object D3();
delegate string D4(object o, params object[] a);
delegate string D5(int i);
class Test
{
    static string F(object o) {...}
    static void G() 
    {
        D1 d1 = F;     // Ok
        D2 d2 = F;     // Ok
        D3 d3 = F;     // Error – nepoužitelné
        D4 d4 = F;     // Error – nepoužitelné v "normální formě"
        D5 d5 = F;     // Error – použitelné, ale nekompatibilní
    }
}

Teď už tedy dokážeme zdůvodnit onu kompilační chybu a také víme, proč můžeme napsat následující.

bool Filter(int number)
{
    return number > 5;
}

var nums = Enumerable.Range(1, 10);
var largerThanFive = nums.Where(Filter);
// namísto
var largerThanFive = nums.Where(i => Filter(i));

Double a decimal zaokrouhlování

double x = Math.Round(2.5); // 2
double y = Math.Round(1.5); // 2

Mě to takhle ve škole teda neučili. Vás ano? Pro vysvětlení jsem si došel na MSDN. Výsledná hodnota operace: “Číslo nejblíže vstupu. Pokud se zlomková část vstupu nachází uprostřed mezi dvěma čísly, pak přednost dostane sudé.” Tomuto se také říká “bankéřovo zaokrouhlování”, které je v souladu s normou IEEE. Je zavedeno za účelem minimalizace zaokrouhlovacích chyb při větším počtu zaokrouhlování. V .NET je používáno jako defaultní, ale lze jej změnit na to, které známe argumentem MidpointRounding.AwayFromZero.

BitArray

Aneb jak ušetřit na bool kolekci. Proměnná typu Boolean v .NET zabere v paměti 1 byte, což je 8x více, než je doopravdy potřeba. Pro uložení true/false nám stačí jediný bit, 0 nebo 1, a zbylých 7 zůstane nevyužito. Důvodem pro toto rozhodnutí je pohodlnější (nikoliv efektivnější) práce s pamětí uvnitř .NET. BitArray je kolekce bitů, která je interně reprezentována polem intů. Jediný Int32 zabere v paměti 4 byty a může tedy držet 32 bitů. Kromě úsporného řešení velkých booleovských kolekcí BitArray nabízí 4 základní bitové operace – And, Or, Not, Xor.

Rozdíly v podmíněných operátorech

Občas se setkám s tím, že někdo pro podmíněné operace AND a OR používá namísto podmíněných operátorů && a || logické operátory & a |. Nutno podotknout, že toto je možné pouze při práci s bool výrazy, jako třeba v if podmínce. Není to vyloženě špatně, ale skrývá to jeden podstatný rozdíl. Zatímco podmíněné operátory využívají tzv. “short circuit” princip, což znamená zkratování, logické operátory ne. Co to pro nás znamená? Např. v následujícím případě, pokud x = false, pak kompilátor ani nebude ověřovat hodnotu y, protože výraz už nemůže být true.

if (x && y) { ... }

Díky tomuto se nedostaneme do problémů, když napíšeme následující podmínku.

if (list != null && list.Count > 0) { ... }

Pokud by se zde druhá část výrazu prováděla a list by náhodou byl null, pak dostaneme NullReferenceException. A to je přesně to, co se stane, pokud budeme používat logické operátory tam, kde nemáme.

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

Duck Typing v .NET

Dalším případem je samozřejmě LINQ, kde stačí existence metod Where, Select, SelectMany apod. se správnou signaturou.

A dalším je objekt, na kterém lze volat await (existence metody GetAwaiter() vracející objekt implementující INotifyCompletion).

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

O GetAwaiter jsem se dozvěděl ještě ten večer, co jsem vydal článek, ale už jsem to nedoplňoval do článku. O LINQ jsem nevěděl, díky.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback