كيف يمكنك إعطاء خاصية C# التلقائية قيمة افتراضية؟

StackOverflow https://stackoverflow.com/questions/40730

  •  09-06-2019
  •  | 
  •  

سؤال

كيف يمكنك إعطاء خاصية C# التلقائية قيمة افتراضية؟إما أن أستخدم المنشئ أو أعود إلى بناء الجملة القديم.

باستخدام المنشئ:

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

باستخدام بناء جملة الملكية العادية (بقيمة افتراضية)

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

هل هناك طريقة أفضل؟

هل كانت مفيدة؟

المحلول

في C# 5 والإصدارات الأقدم، لإعطاء الخصائص التي يتم تنفيذها تلقائيًا قيمة افتراضية، عليك القيام بذلك في المُنشئ.

تم تضمين القدرة على الحصول على مُهيئات خاصية تلقائية منذ الإصدار C# 6.0.بناء الجملة هو:

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

نصائح أخرى

تم التعديل بتاريخ 1/2/15

ج#6 :

باستخدام C# 6، يمكنك تهيئة الخصائص التلقائية مباشرة (أخيرًا!)، وهناك الآن إجابات أخرى في الموضوع تصف ذلك.

ج#5 وما دونه:

على الرغم من أن الاستخدام المقصود للسمة ليس تعيين قيم الخصائص فعليًا، إلا أنه يمكنك استخدام الانعكاس لتعيينها دائمًا على أي حال...

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# حتى 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; }
}

حيث يمنحك هذا تحكمًا واضحًا في قيم الطلب التي يتم تعيينها.

اعتبارًا من C#6 هناك طريقة جديدة:

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

تعمل DefaultValueAttribute فقط في vs Designer.لن يقوم بتهيئة الخاصية لتلك القيمة.

يرى سمة DefaultValue لا تعمل مع الخاصية التلقائية الخاصة بي

أحيانًا أستخدم هذا، إذا كنت لا أريد أن يتم تعيينه فعليًا واستمراره في قاعدة البيانات الخاصة بي:

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

        set { _name = value; } 
    }
}

من الواضح أنه إذا لم تكن سلسلة، فقد أجعل الكائن لاغيًا (double?, int?) وتحقق مما إذا كانت فارغة، أو قم بإرجاع القيمة الافتراضية، أو قم بإرجاع القيمة التي تم تعيينها عليها.

ثم يمكنني إجراء فحص في مستودع التخزين الخاص بي لمعرفة ما إذا كان هذا هو المستودع الافتراضي الخاص بي ولا يستمر، أو إجراء فحص خلفي لمعرفة الحالة الحقيقية لقيمة النسخ الاحتياطي، قبل الحفظ.

امل ان يساعد!

في C# 6.0 يعد هذا أمرًا سهلاً!

يمكنك القيام بذلك في Class الإعلان نفسه، في بيانات إعلان الملكية.

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

بدءًا من الإصدار C#6.0, يمكننا تعيين القيمة الافتراضية للخصائص التي يتم تنفيذها تلقائيًا.

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

يمكننا أيضًا إنشاء خاصية يتم تنفيذها تلقائيًا للقراءة فقط مثل:

public string Name { get; } = "Some Name";

يرى: ج#6:ردود الفعل الأولى، المُهيئات للخصائص التي يتم تنفيذها تلقائيًا - بقلم جون سكيت

في نسخة C # (6.0) وأكبر, ، يمكنك ان تفعل :

لخصائص للقراءة فقط

public int ReadOnlyProp => 2;

لكل من الخصائص القابلة للكتابة والقراءة

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

في الإصدار الحالي من سي # (7.0), ، يمكنك ان تفعل :(يعرض المقتطف بدلاً من ذلك كيف يمكنك استخدام أدوات الوصول للحصول على/تعيين الجسم التعبيري لجعلها أكثر إحكاما عند استخدامها مع حقول الدعم)

private string label = "Default Value";

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

بالإضافة إلى الإجابة المقبولة بالفعل، بالنسبة للسيناريو الذي تريد فيه تحديد خاصية افتراضية على أنها وظيفة من الخصائص الأخرى التي يمكنك استخدامها تدوين الجسم التعبير على C#6.0 (والإصدارات الأحدث) للحصول على بنيات أكثر أناقة وإيجازًا مثل:

public class Person{

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

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

يمكنك استخدام ما سبق بالطريقة التالية

    var p = new Person();

    p.FullName; // First Last

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

    p.FullName; // Jon Snow

لتتمكن من استخدام التدوين "=>" أعلاه، يجب أن تكون الخاصية للقراءة فقط، ولا تستخدم الكلمة الأساسية get accessor.

التفاصيل على MSDN

عينة كاملة صغيرة:

using System.ComponentModel;

private bool bShowGroup ;
[Description("Show the group table"), Category("Sea"),DefaultValue(true)]
public bool ShowGroup
{
    get { return bShowGroup; }
    set { bShowGroup = value; }
}

الحل الخاص بي هو استخدام سمة مخصصة توفر تهيئة خاصية القيمة الافتراضية بواسطة ثابت أو باستخدام مُهيئ نوع الخاصية.

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

لاستخدام هذه السمة، من الضروري وراثة فئة من مُهيئ الفئة الأساسية الخاصة أو استخدام طريقة مساعدة ثابتة:

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

مثال الاستخدام:

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

انتاج:

Item1
(X=3,Y=4)
StringValue

في C# 6 وما فوق، يمكنك ببساطة استخدام بناء الجملة:

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

لاحظ أن لديك readonly الخاصية ببساطة تحذف المجموعة، كما يلي:

public object Foo { get; } = bar;

يمكنك أيضًا تعيين readonly خصائص السيارات من المنشئ.

قبل هذا أجبت على النحو التالي.

سأتجنب إضافة افتراضي إلى المُنشئ؛اترك ذلك للتخصيصات الديناميكية وتجنب وجود نقطتين يتم تعيين المتغير عندهما (أي:النوع الافتراضي وفي المنشئ).عادةً ما أقوم ببساطة بكتابة خاصية عادية في مثل هذه الحالات.

أحد الخيارات الأخرى هو القيام بما يفعله ASP.Net وتحديد الإعدادات الافتراضية عبر سمة:

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

في المنشئ.الغرض من المنشئ هو تهيئة أعضاء البيانات الخاصة به.

هل حاولت استخدام DefaultValueAttribute أو يجب أن يتم تسلسل وإعادة تعيين الأساليب بالاشتراك مع المنشئ؟أشعر أن إحدى هاتين الطريقتين ضرورية إذا كنت تقوم بإنشاء فصل دراسي قد يظهر على سطح المصمم أو في شبكة الخصائص.

public Class ClassName{
    public int PropName{get;set;}
    public ClassName{
        PropName=0;  //Default Value
    }
}
private string name;
public string Name 
{
    get 
    {
        if(name == null)
        {
            name = "Default Name";
        }
        return name;
    }
    set
    {
        name = value;
    }
}

أنا شخصياً لا أرى فائدة من جعلها ملكية على الإطلاق إذا كنت لن تفعل أي شيء على الإطلاق يتجاوز الملكية التلقائية.فقط اتركه كحقل.إن فائدة التغليف لهذه العناصر هي مجرد ذرات حمراء، لأنه لا يوجد شيء وراءها لتغليفه.إذا كنت بحاجة إلى تغيير التنفيذ الأساسي، فلا يزال لديك الحرية في إعادة هيكلتها كخصائص دون كسر أي تعليمات برمجية تابعة.

همم...ربما سيكون هذا موضوع سؤاله الخاص لاحقًا

للتوضيح، نعم، تحتاج إلى تعيين القيم الافتراضية في المُنشئ للكائنات المشتقة من الفئة.سوف تحتاج إلى التأكد من وجود المُنشئ باستخدام مُعدِّل الوصول المناسب للبناء في مكان استخدامه.إذا لم يتم إنشاء مثيل للكائن، على سبيل المثال.ليس لديه منشئ (على سبيل المثال.طرق ثابتة) ثم يمكن تعيين القيمة الافتراضية بواسطة الحقل.السبب هنا هو أن الكائن نفسه سيتم إنشاؤه مرة واحدة فقط ولا تقم بإنشاء مثيل له.

@ Darren Kopp - إجابة جيدة ونظيفة وصحيحة.وللتذكير، يمكنك كتابة مُنشئات للطرق المجردة.تحتاج فقط إلى الوصول إليها من الفئة الأساسية عند كتابة المُنشئ:

منشئ في الفئة الأساسية:

public BaseClassAbstract()
{
    this.PropertyName = "Default Name";
}

منشئ في المشتقة / الخرسانة / الفئة الفرعية:

public SubClass() : base() { }

النقطة هنا هي أن متغير المثيل المستمد من الفئة الأساسية قد يدفن اسم الحقل الأساسي الخاص بك.تعيين قيمة الكائن الحالية التي تم إنشاء مثيل لها باستخدام "هذا". سيسمح لك بتكوين كائنك بشكل صحيح فيما يتعلق بالمثيل الحالي ومستويات الإذن المطلوبة (معدلات الوصول) حيث تقوم بتسهيله.

استخدم المنشئ لأنه "عند انتهاء المنشئ، يجب أن ينتهي البناء".الخصائص تشبه الحالات التي تحملها فئاتك، إذا كان عليك تهيئة حالة افتراضية، فستفعل ذلك في المُنشئ الخاص بك.

يمكنك وضع مثل هذا ببساطة

    public sealed  class Employee
{
    public int Id { get; set; } = 101;
}
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; }
}

أعتقد أن هذا من شأنه أن يفعل ذلك من أجل إعطاء SomeFlag افتراضيًا خطأ.

private bool _SomeFlagSet = false;
public bool SomeFlag
{
    get
    {
        if (!_SomeFlagSet)
            SomeFlag = false;        

        return SomeFlag;
    }
    set
    {
        if (!_SomeFlagSet)
            _SomeFlagSet = true;

        SomeFlag = value;        
    }
}

التهيئة في السطر، يعد استخدام المنشئات للتهيئة ممارسة سيئة وسيؤدي إلى المزيد من التغييرات العاجلة لاحقًا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top