Domanda

Ho diverse domande sui contratti di codice e le migliori pratiche per il loro utilizzo. Diciamo che abbiamo una classe, con diverse proprietà (vedi sotto per esempio):

class Class1
{
    // Fields
    private string _property1;       //Required for usage
    private List<object> _property2; //Not required for usage

    // Properties
    public string Property1 
    { 
        get
        {
            return this._property1;
        }            
        set
        {
            Contract.Requires(value != null);
            this._property1 = value;
        } 
    }
    public List<object> Property2 
    { 
        get
        {
            return this._property2;
        }
        set
        {
            Contract.Requires(value != null);
            this._property2 = value;
        }
    }

    public Class1(string property1, List<object> property2)
    {
        Contract.Requires(property1 != null);
        Contract.Requires(property2 != null);

        this.Property1 = property1;
        this.Property2 = property2;
    }

    public Class1(string property1)
        : this(property1, new List<object>())
    { }
}

Qualche spiegazione su ciò che voglio ottenere:

(a) Property1 è un campo richiesto. Property2 non è esplicitamente richiesto per il normale utilizzo dell'oggetto.

Ho le seguenti domande:

  1. Dovrei nemmeno preoccuparmi dei contratti per Property2; Poiché Property2 non è un campo richiesto, se avesse un contratto. La posa di un contratto su proprietà2 indica che è effettivamente richiesto per il normale utilizzo dell'oggetto;

  2. Anche se Property2 non è esplicitamente richiesto, non vi è alcun motivo per cui sia nullo, quindi il contratto definito presso il setter. La definizione del contratto su Property2 ridurrebbe gli assegni null nel codice di chiamata? Ciò dovrebbe ridurre i bug e migliorare la manutenibilità del codice: questa ipotesi è corretta?

  3. Se è giusto, come posso assicurarmi di chiamare il codice che Property2 non sarà mai nullo? Uso contratt.invariant (proprietà2! = Null); o contratt.ensures (proprietà2! = null) nel costruttore o contratt.ensures (proprietà2! = null) in init () o contract.ensures (proprietà! = null) nel setter? (cioè se si utilizza contratt.ensures (proprietà2! = null), dove viene posizionato)?

Mi scuso se le domande sembrano semplici. Sto solo cercando pensieri sulla questione e ciò che voi gente considerate le migliori pratiche.

È stato utile?

Soluzione

Questo è ciò che consiglierei per quanto riguarda i contratti:

    class Class1
    {
        // Fields
        private string _property1;       //Required for usage
        private List<object> _property2; //Not required for usage

        // Properties
        public string Property1
        {
            get
            {
                Contract.Ensures(Contract.Result<string>() != null);
                return this._property1;
            }
            set
            {
                Contract.Requires(value != null);
                this._property1 = value;
            }
        }

        public List<object> Property2
        {
            get
            {
                Contract.Ensures(Contract.Result<List<object>>() != null);
                return this._property2;
            }
            set
            {
                Contract.Requires(value != null);
                this._property2 = value;
            }
        }

        public Class1(string property1, List<object> property2)
        {
            Contract.Requires(property1 != null);
            Contract.Requires(property2 != null);

            this.Property1 = property1;
            this.Property2 = property2;
        }

        public Class1(string property1)
            : this(property1, new List<object>())
        {
            Contract.Requires(property1 != null);
        }

        [ContractInvariantMethod]
        private void ContractInvariants()
        {
            Contract.Invariant(_property1 != null);
            Contract.Invariant(_property2 != null);
        }
    }

Le proprietà hanno i loro contratti comportamentali pubblici e gli invarianti cattureranno eventuali errori che potresti introdurre in seguito mentre aggiungi logica alla Classe1 che potrebbe modificare i valori sul campo e quindi violare i contratti pubblici. In alternativa, se i campi possono essere resi prontamente (e i setter rimossi), non è necessario gli invarianti.

Altri suggerimenti

Penso che ci siano molte preferenze personali qui, ma i miei 2 centesimi ...

1) Vorrei, e probabilmente sarei tentato di regolare i costruttori per avere proprietà2 come argomento opzionale:

Class1(string property1, List<object> property2 = new List<object>())      
{      
    Contract.Requires(property1 != null);      
    Contract.Requires(property2 != null);      

    this.Property1 = property1;      
    this.Property2 = property2;      
} 

2) Vedi 3

3) Prima di essere felice di ridurre i controlli nulli nel codice di chiamata, preferirei personalmente vedere un

Contract.Ensures(property2 != null) 

Sul getter - Ho visto VS11 CTP mostra i contratti nelle punte di strumenti per le definizioni, quindi riuscire a vedere questo che vorrei sapere che non ho bisogno di verificare la presenza di null. Conserverei il Requires sul setter.

Spesso quando si dispone di un elenco in un oggetto, avrai l'oggetto che si prende cura della creazione. Quindi, se un consumatore desidera aggiungere a un elenco, deve prima ottenerlo. A seconda dell'uso di questa classe, questa potrebbe essere il percorso migliore.

class Class1
{
    // Fields
    private string _property1;       //Required for usage
    private List<object> _property2 = new List<object>(); //Not required for usage

    // Properties
    public string Property1 
    { 
        get
        {
            return this._property1;
        }            
        set
        {
            Contract.Requires(value != null);
            this._property1 = value;
        } 
    }
    public List<object> Property2 
    { 
        get
        {
            return this._property2;
        }
        //We don't have a setter.
    }

    public Class1(string property1)
    {
        Contract.Requires(property1 != null);

        this.Property1 = property1;
    }
}

1/2: avere un contratto su proprietà2 è appropriato se questo è il comportamento che si desidera far rispettare, non dovrebbe essere nullo e ridurrà sicuramente la necessità di controlli nulli e potenziali bug attorno ai null.

3: per rispondere a questa domanda ho riscritto la tua classe come segue

class Class1
{
    //Properties
    public string Property1 { get; set; }
    public List<object> Property2 { get; set; }

    public Class1(string property1, List<object> property2)
    {
        Contract.Requires(property1 != null);
        Contract.Requires(property2 != null);

        Property1 = property1;
        Property2 = property2;
    }

    public Class1(string property1)
        : this(property1, new List<object>())
    { 
        Contract.Requires(property1 != null);
    }

    [ContractInvariantMethod]
    private void ObjectInvariant()
    {
        Contract.Invariant(Property1 != null);
        Contract.Invariant(Property2 != null);
    }
}

Quando hai proprietà implementate automatiche nella tua classe puoi utilizzare Contract.Invariant(). Ciò rende i contratti espliciti nella tua proprietà ridondante, quindi non è necessario per il seguente codice ora.

public string Property1 
{ 
    get
    {
        Contract.Ensures(Contract.Result<string>() != null);
        return this._property1;
    }            
    set
    {
        Contract.Requires(value != null);
        this._property1 = value;
    } 
}

Ciò si occuperà della protezione della proprietà. Per garantire completamente che la proprietà non sia mai null, aggiungerai un contratto.Requires (Property1! = Null) nel costruttore.

So che questa risposta è in ritardo di 3 anni, ma potrebbe essere utile per te!

Fonte: http://research.microsoft.com/en-us/projects/contracts/userdoc.pdf

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top