Convalida dei dati in un costruttore c #

0

Sto solo cercando di imparare c # e sono un po 'confuso sul modo migliore per convalidare i dati che vengono inseriti da un utente.

Ho questo codice in una classe persona per convalidare un numero telefonico

public string PhoneNumber 
{
    get { return phoneNumber; }
    set
    {
        if (!Regex.IsMatch(value, @"^[0-9()+- ]+$"))
        {
            throw new ArgumentException("Error Phonenumber: must contain numbers 0-9 & characters ()+-");
        }

        phoneNumber = value;
    }
}

Il problema è che quando questo viene eseguito interrompe il programma, tutto ciò che voglio è dire all'utente che i dati di input non sono corretti, interrompere la creazione dell'oggetto e lasciare che l'utente corregga il suo errore e riprovare.

Ho letto molte domande online che sono simili ma niente mi sta dando una risposta concreta per risolvere questo problema.

Grazie in anticipo.

    
posta Michael Williams 31.07.2016 - 03:01
fonte

4 risposte

2

Il primo passo è convalidare l'input dell'utente prima passandolo al costruttore. Ciò garantisce il massimo controllo su come vengono gestiti gli errori di convalida.

Quando si convalida all'interno del costruttore e si genera un'eccezione in caso di errore, il codice chiamante deve racchiudere la costruzione dell'oggetto in un blocco try e rilevare l'eccezione. I problemi di ambito rendono il codice per questo più complesso se si desidera una gestione complessa degli errori, è più adatto per l'uso insieme alla convalida prima dell'uso. Gli errori dovrebbero quindi essere catturati dalla convalida e il codice non arriverebbe mai al punto in cui ha costruito l'oggetto, un'eccezione al di fuori del costruttore sarebbe stata trattata come un errore logico dell'applicazione catastrofico. Il codice che semplifica la creazione dell'applicazione è molto più semplice e i guasti del codice di convalida che causano un'eccezione saranno abbastanza rari da essere accettabili.

    
risposta data 31.07.2016 - 03:43
fonte
1

Odio trattare con oggetti che possono entrare in stati non validi. Se puoi progettare ragionevolmente un oggetto in modo che lo stato non sia semplicemente invalido, fallo per favore.

Ciò significa che devi controllare tutto ciò che può cambiare lo stato dell'oggetto e in qualche modo non consentire la modifica. O lanciando un'eccezione o insistendo sul fatto che lo stato non cambia. Mentre odio le cose che falliscono silenziosamente a volte hanno senso. Premendo il pulsante su quando l'ascensore si trova all'ultimo piano, non è necessario spegnere l'ascensore o appoggiarlo sul tetto. Ma nel tuo caso lanciare un'eccezione è del tutto corretta. Almeno corretto come il tuo regex comunque.

    
risposta data 31.07.2016 - 05:01
fonte
0

Un'alternativa al flusso di lavoro try-throw-catch - che ha alcuni problemi con l'essere abbastanza complesso da seguire a volte - consiste nell'utilizzare un approccio più funzionale, in cui i possibili "errori" sono dichiarati nel tipo di ritorno. Ora, so che questo non è sempre facile da emulare in C #, ma una soluzione abbastanza pulita è quella di fare una sorta di funzione "fabbrica" per i numeri di telefono che hanno la firma Either<PhoneNumberValidationError, PhoneNumber> CreatePhoneNumber(string number) , e quindi si FORCE il codice chiamante per considerare l'errore Astuccio. La sintassi non deve essere troppo confusa (puoi sostanzialmente ricreare la corrispondenza del modello usando% delegati% di%, ma mi rendo conto che è una specie di lotta contro il linguaggio), ma potrebbe non essere il C% idiomatico al 100% (ancora;) ).

È comunque un approccio interessante da prendere in considerazione, e se non altro penso che sia uno degli argomenti per guardare a ciò che F # può offrire.

    
risposta data 31.07.2016 - 12:24
fonte
0

Il lancio nella classe che mostri è soddisfacente, anche nel costruttore. Questo è spesso disapprovato in C ++ per vari motivi, tuttavia, funziona bene in C #.

Tuttavia dovrai scrivere un codice aggiuntivo per colmare il divario tra l'arresto del costruttore e l'arresto del programma: questo bridge potrebbe informare l'utente reale che l'input non è valido e riprendere a uno stato precedente del programma, chiedendo l'input .

Questo gap è colmato come @RobertHarvey menziona, con una presa, da un certo chiamante (o chiamante del chiamante).

Tuttavia, non è necessario che il chiamante immediato cerchi le eccezioni. Se tutti i chiamanti immediati hanno bloccato tutte le chiamate in try / catch, probabilmente staremmo meglio senza eccezioni (utilizzando invece i codici di errore).

Dovresti cercare di identificare i posti nel codice chiamante dove il fallimento significa che sei in grado di riprovare in qualche modo. C'è un po 'di arte in questo, ma dovresti cercare di pensare in termini di alto livello sul lavoro o parte del lavoro che può essere interrotta quando qualcosa va storto, e cosa potresti voler riprendere o ripetere in questi casi. A volte, pensare a cosa il programma dovrebbe fare dopo, indipendentemente dal fatto che qualche lavoro abbia esito positivo o positivo, ti aiuterà a dirti dove gestire gli errori.

Nella tua domanda stai già indicando l'attività di livello superiore che vuoi riprovare. Ora, potrebbe essere che tu abbia già un loop di alto livello nel tuo programma in modo che quando ottiene un buon input e abbia successo, non si fermi semplicemente, ma chieda all'utente un altro input su cui lavorare. Se non si dispone di un ciclo di questo tipo, è possibile aggiungerne uno. Quel loop di alto livello può essere un buon posto per localizzare il pescato. Ogni iterazione del ciclo ha esito positivo (e informa l'utente e riparte) oppure non riesce e, in caso di errore, intercetta e informa l'utente, quindi torna di nuovo.

    
risposta data 31.07.2016 - 17:51
fonte

Leggi altre domande sui tag