automatic-properties use - Come si fornisce a C#Auto-Property un valore predefinito?




how define (19)

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?


Answers

Quando si allinea un valore iniziale per una variabile, verrà comunque eseguito implicitamente nel costruttore.

Direi che questa sintassi era la migliore pratica in C # fino a 5:

class Person 
{
    public Person()
    {
        //do anything before variable assignment

        //assign initial values
        Name = "Default Name";

        //do anything after variable assignment
    }
    public string Name { get; set; }
}

Dato che questo ti dà un chiaro controllo dell'ordine, i valori vengono assegnati.

A partire da C # 6 c'è un nuovo modo:

public string Name { get; set; } = "Default Name"


In C # 5 e precedenti, per dare alle proprietà auto implementate un valore predefinito, devi farlo in un costruttore.

La possibilità di avere inizializzatori di proprietà automatici è inclusa da C # 6.0. La sintassi è:

public int X { get; set; } = x; // C# 6 or higher

Oltre alla risposta già accettata, per lo scenario in cui si desidera definire una proprietà predefinita come funzione di altre proprietà è possibile utilizzare la notazione del corpo dell'espressione su C # 6.0 (e successive) per costrutti ancora più eleganti e concisi come:

public class Person{

    public string FullName  => $"{First} {Last}"; // expression body notation

    public string First { get; set; } = "First";
    public string Last { get; set; } = "Last";
}

Puoi usare quanto sopra nel modo seguente

    var p = new Person();

    p.FullName; // First Last

    p.First = "Jon";
    p.Last = "Snow";

    p.FullName; // Jon Snow

Per poter utilizzare la notazione "=>" sopra, la proprietà deve essere di sola lettura e non si utilizza la parola chiave get accessor.

Dettagli su MSDN


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; 
 }

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


private string name;
public string Name 
{
    get 
    {
        if(name == null)
        {
            name = "Default Name";
        }
        return name;
    }
    set
    {
        name = value;
    }
}

inizializzare in linea, usare i costruttori per inizializzarsi è una cattiva pratica e porterà a ulteriori cambiamenti 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.


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

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; }
}

A volte lo uso, se non voglio che sia effettivamente impostato e persistito nel mio db:

class Person
{
    private string _name; 
    public string Name 
    { 
        get 
        {
            return string.IsNullOrEmpty(_name) ? "Default Name" : _name;
        } 

        set { _name = value; } 
    }
}

Ovviamente, se non è una stringa, potrei rendere nullable l'oggetto (double ?, int?) E controllare se è null, restituire un valore predefinito o restituire il valore su cui è impostato.

Quindi posso fare un check nel mio repository per vedere se è il mio default e non persistere, o fare un backdoor check per vedere lo stato reale del valore di backing, prima di salvare.

Spero che sia d'aiuto!


Nel costruttore. Lo scopo del costruttore è inizializzare i suoi membri dei dati.


In C # 6.0 questo è un gioco da ragazzi!

Puoi farlo nella stessa dichiarazione di Class , nelle dichiarazioni di dichiarazione di proprietà.

public class Coordinate
{ 
    public int X { get; set; } = 34; // get or set auto-property with initializer

    public int Y { get; } = 89;      // read-only auto-property with initializer

    public int Z { get; }            // read-only auto-property with no initializer
                                     // so it has to be initialized from constructor    

    public Coordinate()              // .ctor()
    {
        Z = 42;
    }
}


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; }
}


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


Recentemente ho fatto una cosa molto simile per creare un metodo OnPropertyChanged sicuro.

Ecco un metodo che restituirà l'oggetto PropertyInfo per l'espressione. Genera un'eccezione se l'espressione non è una proprietà.

public PropertyInfo GetPropertyInfo<TSource, TProperty>(
    TSource source,
    Expression<Func<TSource, TProperty>> propertyLambda)
{
    Type type = typeof(TSource);

    MemberExpression member = propertyLambda.Body as MemberExpression;
    if (member == null)
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a method, not a property.",
            propertyLambda.ToString()));

    PropertyInfo propInfo = member.Member as PropertyInfo;
    if (propInfo == null)
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a field, not a property.",
            propertyLambda.ToString()));

    if (type != propInfo.ReflectedType &&
        !type.IsSubclassOf(propInfo.ReflectedType))
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a property that is not from type {1}.",
            propertyLambda.ToString(),
            type));

    return propInfo;
}

Il parametro source viene utilizzato in modo che il compilatore possa eseguire l'inferenza del tipo sulla chiamata al metodo. Puoi fare quanto segue

var propertyInfo = GetPropertyInfo(someUserObject, u => u.UserID);






c# automatic-properties