c# Come si fornisce a C # Auto-Property un valore predefinito?





11 Answers

Modifica 1/2/15

Con C # 6 puoi inizializzare direttamente le proprietà automatiche (finalmente!), Ora ci sono altre risposte nel thread che lo descrivono.

Per C # 5 e seguenti:

Anche se l'uso previsto dell'attributo non è quello di impostare effettivamente i valori delle proprietà, è possibile utilizzare la riflessione per impostarli sempre comunque ...

public class DefaultValuesTest
{    
    public DefaultValuesTest()
    {               
        foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this))
        {
            DefaultValueAttribute myAttribute = (DefaultValueAttribute)property.Attributes[typeof(DefaultValueAttribute)];

            if (myAttribute != null)
            {
                property.SetValue(this, myAttribute.Value);
            }
        }
    }

    public void DoTest()
    {
        var db = DefaultValueBool;
        var ds = DefaultValueString;
        var di = DefaultValueInt;
    }


    [System.ComponentModel.DefaultValue(true)]
    public bool DefaultValueBool { get; set; }

    [System.ComponentModel.DefaultValue("Good")]
    public string DefaultValueString { get; set; }

    [System.ComponentModel.DefaultValue(27)]
    public int DefaultValueInt { get; set; }
}
c# automatic-properties

Come si fornisce a C # Auto-Property un valore predefinito? Uso il costruttore o ripristino la vecchia sintassi.

Usando il Costruttore:

class Person 
{
    public Person()
    {
        Name = "Default Name";
    }
    public string Name { get; set; }
}

Uso della sintassi di proprietà normale (con un valore predefinito)

private string name = "Default Name";
public string Name 
{
    get 
    {
        return name;
    }
    set
    {
        name = value;
    }
}

Esiste un modo migliore?










In Version of C # (6.0) e versioni successive, puoi fare:

Per proprietà di sola lettura

public int ReadOnlyProp => 2;

Per entrambe le proprietà scrivibili e leggibili

public string PropTest { get; set; } = "test";

Nella versione corrente di C # (7.0) , puoi fare: (Lo snippet mostra piuttosto come usare gli accessi get / set con corpo dell'espressione per renderlo più compatto quando si usa con i campi di supporto)

private string label = "Default Value";

// Expression-bodied get / set accessors.
public string Label
{
   get => label;
   set => this.label = value; 
 }



La mia soluzione è di usare un attributo personalizzato che fornisce l'inizializzazione della proprietà del valore di default tramite costante o utilizzando l'inizializzatore del tipo di proprietà.

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class InstanceAttribute : Attribute
{
    public bool IsConstructorCall { get; private set; }
    public object[] Values { get; private set; }
    public InstanceAttribute() : this(true) { }
    public InstanceAttribute(object value) : this(false, value) { }
    public InstanceAttribute(bool isConstructorCall, params object[] values)
    {
        IsConstructorCall = isConstructorCall;
        Values = values ?? new object[0];
    }
}

Per utilizzare questo attributo è necessario ereditare una classe da un inizializzatore speciale della classe base o utilizzare un metodo di supporto statico:

public abstract class DefaultValueInitializer
{
    protected DefaultValueInitializer()
    {
        InitializeDefaultValues(this);
    }

    public static void InitializeDefaultValues(object obj)
    {
        var props = from prop in obj.GetType().GetProperties()
                    let attrs = prop.GetCustomAttributes(typeof(InstanceAttribute), false)
                    where attrs.Any()
                    select new { Property = prop, Attr = ((InstanceAttribute)attrs.First()) };
        foreach (var pair in props)
        {
            object value = !pair.Attr.IsConstructorCall && pair.Attr.Values.Length > 0
                            ? pair.Attr.Values[0]
                            : Activator.CreateInstance(pair.Property.PropertyType, pair.Attr.Values);
            pair.Property.SetValue(obj, value, null);
        }
    }
}

Esempio di utilizzo:

public class Simple : DefaultValueInitializer
{
    [Instance("StringValue")]
    public string StringValue { get; set; }
    [Instance]
    public List<string> Items { get; set; }
    [Instance(true, 3,4)]
    public Point Point { get; set; }
}

public static void Main(string[] args)
{
    var obj = new Simple
        {
            Items = {"Item1"}
        };
    Console.WriteLine(obj.Items[0]);
    Console.WriteLine(obj.Point);
    Console.WriteLine(obj.StringValue);
}

Produzione:

Item1
(X=3,Y=4)
StringValue



In C # 6 e sopra puoi semplicemente usare la sintassi:

public object Foo { get; set; } = bar;

Nota che per avere una proprietà readonly semplicemente omettere il set, in questo modo:

public object Foo { get; } = bar;

È anche possibile assegnare proprietà auto di readonly dal costruttore.

Prima di questo ho risposto come di seguito.

Eviterei di aggiungere un valore predefinito al costruttore; Lasciare questo per incarichi dinamici ed evitare di avere due punti a cui è assegnata la variabile (cioè il tipo di default e nel costruttore). Normalmente scriverei semplicemente una proprietà normale in questi casi.

Un'altra opzione è fare ciò che fa ASP.Net e definire i valori di default tramite un attributo:

http://msdn.microsoft.com/en-us/library/system.componentmodel.defaultvalueattribute.aspx







Personalmente, non vedo assolutamente il senso di renderlo una proprietà se non hai intenzione di fare qualcosa oltre la proprietà auto. Lasciatelo come un campo. Il vantaggio di incapsulamento per questi oggetti è solo una falsa pista, perché non c'è niente dietro a loro da incapsulare. Se hai sempre bisogno di modificare l'implementazione sottostante, sei comunque libero di rifattenerli come proprietà senza rompere alcun codice dipendente.

Hmm ... forse questo sarà l'oggetto della sua stessa domanda in seguito




Usa il costruttore perché "Quando il costruttore è finito, la costruzione dovrebbe essere finita". le proprietà sono come gli stati delle tue classi, se dovessi inizializzare uno stato predefinito, lo faresti nel tuo costruttore.




class Person 
{    
    /// Gets/sets a value indicating whether auto 
    /// save of review layer is enabled or not
    [System.ComponentModel.DefaultValue(true)] 
    public bool AutoSaveReviewLayer { get; set; }
}



inizializzare in linea, usare i costruttori per inizializzarsi è una cattiva pratica e porterà a ulteriori cambiamenti in seguito.




Related