it-swarm-eu.dev

Ověření parametrů konstruktoru v C # - Doporučené postupy

Jaký je nejlepší postup pro ověřování parametrů konstruktoru?

Předpokládejme jednoduchý kousek C #:

public class MyClass
{
    public MyClass(string text)
    {
        if (String.IsNullOrEmpty(text))
            throw new ArgumentException("Text cannot be empty");

        // continue with normal construction
    }
}

Bylo by vhodné zahodit výjimku?

Alternativou, se kterou jsem se setkal, byla předběžná validace, než došlo k okamžitému provedení:

public class CallingClass
{
    public MyClass MakeMyClass(string text)
    {
        if (String.IsNullOrEmpty(text))
        {
            MessageBox.Show("Text cannot be empty");
            return null;
        }
        else
        {
            return new MyClass(text);
        }
    }
}
35
MPelletier

Mám tendenci provádět všechny své validace v konstruktoru. To je nutné, protože téměř vždy vytvářím neměnné objekty. Pro váš konkrétní případ si myslím, že je to přijatelné.

if (string.IsNullOrEmpty(text))
    throw new ArgumentException("message", "text");

Pokud používáte .NET 4, můžete to udělat. To samozřejmě záleží na tom, zda považujete řetězec, který obsahuje pouze prázdné místo, za neplatný.

if (string.IsNullOrWhiteSpace(text))
    throw new ArgumentException("message", "text");
27
ChaosPandion

Mnoho lidí tvrdí, že konstruktéři by neměli vyhazovat výjimky. Například KyleG na na této stránce to dělá. Upřímně, nedokážu vymyslet důvod, proč ne.

V C++ je vyvolání výjimky z konstruktoru špatný nápad, protože vám ponechává přidělenou paměť obsahující neinicializovaný objekt, na který nemáte žádný odkaz (tj. Je to klasický únik paměti). Odtud pochází stigma - banda vývojářů C++ ze staré školy napůl přerušilo učení C # a právě na to aplikovalo to, co věděli z C++. Naproti tomu v Objective-C Apple oddělil krok přidělení od kroku inicializace, takže konstruktéři v tomto jazyce mohou hodit výjimky.

C # nemůže uniknout paměti z neúspěšného volání konstruktoru. Dokonce i některé třídy v rámci .NET framework budou ve svých konstruktorech vyvolávat výjimky.

23
Ant

Vyhodit výjimku IFF třídy nelze uvést do konzistentního stavu s ohledem na její sémantické použití. Jinak ne. NIKDY nedovolte, aby objekt existoval v nekonzistentním stavu. To zahrnuje neposkytování úplných konstruktérů (jako kdybyste měli prázdný konstruktor + inicializovali () dříve, než bude váš objekt skutečně postaven) ... JUST SAY NO!

V špetce to udělá každý. Udělal jsem to druhý den pro velmi úzce používaný objekt v těsném rozsahu. Jednoho dne po cestě já nebo někdo jiný pravděpodobně zaplatím za tento skluz v praxi cenu.

Měl bych poznamenat, že „konstruktorem“ mám na mysli to, co klient volá, aby objekt postavil. Stejně snadno by to mohlo být něco jiného než skutečný konstrukt, který se nazývá „Konstruktor“. Například něco podobného v C++ by neporušilo zásadu IMNSHO:

struct funky_object
{
  ...
private:
  funky_object();
  bool initialize(std::string);

  friend boost::optional<funky_object> build_funky(std::string);
};
boost::optional<funky_object> build_funky(std::string str)
{
  funky_object fo;
  if (fo.initialize(str)) return fo;
  return boost::optional<funky_object>();
}

Od jediného způsobu, jak vytvořit funky_object je voláním build_funky princip nikdy nepovolit existenci neplatného objektu zůstává nedotčen, i když skutečný „konstruktor“ úlohu nedokončí.

To je ale spousta práce navíc pro pochybný zisk (možná i ztrátu). Stále bych upřednostňoval výjimečnou trasu.

13
Edward Strange

V tomto případě bych použil tovární metodu. V zásadě nastavte třídu pouze pro soukromé konstruktory a použijte tovární metodu, která vrací instanci vašeho objektu. Pokud jsou počáteční parametry neplatné, stačí vrátit hodnotu null a nechat volající kód rozhodnout, co dělat.

public class MyClass
{
    private MyClass(string text)
    {
        //normal construction
    }

    public static MyClass MakeMyClass(string text)
    {
        if (String.IsNullOrEmpty(text))
            return null;
        else
            return new MyClass(text);
    }
}
public class CallingClass
{
    public MyClass MakeMyClass(string text)
    {
        var cls = MyClass.MakeMyClass(text);
        if(cls == null)
             //show messagebox or throw exception
        return cls;
    }
}

Nikdy nevyhazujte výjimky, pokud nejsou podmínky výjimečné. Myslím, že v tomto případě lze prázdnou hodnotu snadno předat. Pokud by tomu tak bylo, použití tohoto vzorce by zabránilo výjimkám a pokutám za výkon, které jim vzniknou při zachování stavu MyClass.

9
Donn Relacion
  • Konstruktor by neměl mít žádné vedlejší účinky.
    • Jako vedlejší účinek by mělo být považováno cokoli jiného než inicializace soukromého pole.
    • Konstruktor s vedlejšími účinky porušuje princip Single-Responsibilty-Princip (SRP) a je v rozporu s duchem objektově orientovaného programování (OOP).
  • Konstruktor by měl být lehký a neměl by nikdy selhat.
    • Například vždycky se chvěním, když vidím blok try-catch uvnitř konstruktoru. Konstruktor by neměl vyvolávat výjimky nebo chyby protokolu.

Dalo by se rozumně zpochybnit tyto pokyny a říci: „Ale tato pravidla nedodržuji a můj kód funguje dobře!“ K tomu bych odpověděl: „No, to může být pravda, dokud tomu tak není.“

  • Výjimky a chyby uvnitř konstruktoru jsou velmi neočekávané. Pokud jim nebude řečeno, budoucí programátoři nebudou mít sklon obklopovat tato volání konstruktorů defenzivním kódem.
  • Pokud něco selže ve výrobě, generované trasování zásobníku může být obtížné analyzovat. Horní část trasování zásobníku může ukazovat na volání konstruktoru, ale ve konstruktoru se děje mnoho věcí a nemusí ukazovat na skutečný LOC, který selhal.
    • Tam, kde tomu tak bylo, jsem analyzoval mnoho tras .NET stacků.
2
Jim G.

Moje preference je nastavit výchozí, ale vím, že Java má knihovnu „Apache Commons“, která něco podobného dělá, a myslím, že je to docela dobrý nápad. Nevidím problém s vyvoláním výjimky, pokud by neplatná hodnota uvedla objekt do nepoužitelného stavu; řetězec není dobrým příkladem, ale co kdyby to bylo pro DI chudého člověka? Nemohli byste operovat, pokud hodnota null byl předán místo, řekněme, rozhraní ICustomerRepository. V situaci, jako je tato, je vyvolání výjimky správným způsobem řešení věcí, řekl bych.

0
Wayne Molina

Záleží na tom, co dělá MyClass. Pokud byla MyClass ve skutečnosti třída datových úložišť a text parametru byl připojovací řetězec, nejlepší praxí by bylo vyhodit ArgumentException. Pokud je však MyClass třída StringBuilder (například), můžete ji prostě nechat prázdnou.

Takže záleží na tom, jak důležitý je text parametru pro metodu - má objekt smysl s nulovou nebo prázdnou hodnotou?

0
Steven Striga