Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get default compile-time value of Auto-Implemented property C# 6.0 after it changed?

Shortly, The new C# 6.0 Auto-Implemented Property allows us to make this

    public static bool IsSoundEffects { get; set; } = true;   // C# 6.0 allows this

Now in somewhere, I changed the property IsSoundEffects = false, So accessing it will be false.

hmm, So how to get the actual real default compile-time auto-implemented property value.

Something Like :
Type.GetPropertyDefaultValue(IsSoundEffects); // A real compile-time one = true

OR

default(IsSoundEffects)   // idk, something like that

Why I need that?

because I filling the properties from the database. and restore it if user need to restore the default values. for example settings.

Looks strange? I searched enough but all examples about the auto-implemented feature did not restore the default value.

Edited

The best approaches provided by

xiangbin.pang answer for reflection way [Short-One]

Christopher answers for constants as default values.

like image 257
deveton Avatar asked Nov 17 '19 23:11

deveton


3 Answers

  1. For instance property, just new an instance then get the default property value is the easiest way.
  2. For static property, the default value can be preserved in the static constructor.
    public static class MyClass
    {
        public static int MyProp1 { get; set; } = 100;
        public static bool MyProp2 { get; set; } = false;

        private static Dictionary<string, object> defaultValues;

        static MyClass()
        {
            defaultValues = new Dictionary<string, object>();

            foreach(var prop in typeof(MyClass).GetProperties(BindingFlags.Static| BindingFlags.Public | BindingFlags.NonPublic))
            {
                defaultValues[prop.Name] = prop.GetValue(null);
            }
        }

        public static (T,bool) GetDefault<T>(string propName)
        {
            if(defaultValues.TryGetValue(propName, out object value))
            {
                return ((T)(value), true);
            }
            return (default, false);
        }
    }

    //test codes
    static void Main(string[] args)
    {

        MyClass.MyProp1 = 1000;
        MyClass.MyProp2 = true;

        var defaultValueOrProp1 = MyClass.GetDefault<int>("MyProp1");
        if(defaultValueOrProp1.Item2)
        {
            Console.WriteLine(defaultValueOrProp1.Item1);//100
        }

        var defaultValueOrProp2 = MyClass.GetDefault<bool>("MyProp2");
        if (defaultValueOrProp2.Item2)
        {
            Console.WriteLine(defaultValueOrProp2.Item1);//false
        }
    }



Following Line added by question author:

For setting property with default value

private static void ResetPropertyValue(string PropertyName)
{ 
    typeof(Options).GetProperty(PropertyName).SetValue(null, 
    defaultValues[PropertyName]);
}
like image 174
xiangbin.pang Avatar answered Oct 20 '22 16:10

xiangbin.pang


Properties are little more then Syntax sugar for get/set function pairs. And what you got there is little more then a basic, bog-standart assignment/function call around when the constructor runs. As all literals and constatns, it should no longer exist at runtime.

The naive way would be to have a constant like IsSoundEffectsDefaultValue. And I do think that is sufficient in most cases.

There is a off-chance that a old idea of mine might apply to your broader problem. I need to search for the code however.

Edit:

I could not find my old code, unfortunately. I can re-create it however. The basic idea is to have multiple "layers" of values, with one value hiding (but not overwriting) the other.

public class defaultAble<T>{
    readonly T defaultValue;

    //constructor
    public defaultAble(T defaultValue){
        this.defaultValue = defaultValue;
        //First set the value
        RestoreDefault();
    }

    public RestoreDefault(){
        value = this.defaultValue;
    }

    public T value { get; set; }
}

Edit 2:

There might be a even better solution in WPF/MVVM circles. A lot of MVVM is writing properties with the same code - one that raises change notification by INotifyPropertyChanged. And a big issue with Properties is not writing the backing field by accident.

Some people figured out solutions like putting the actuall value and all the code into a something like a Dictionar<string, object> mostly automagically. I am not sure about the details, however. Also DependancyProperties might get close to it too.

like image 27
Christopher Avatar answered Oct 20 '22 16:10

Christopher


One approach you could use would be based on Custom Attributes. You could define as Custom Attribute to hold the default value. For example,

public class DefaultValueAttribute:Attribute
{
    public object DefaultValue{get;set;}
    public DefaultValueAttribute(object defaultValue)=>DefaultValue = defaultValue;
}

You can now use the Attribute to store the default value as

public static class SomeClass
{
  [DefaultValueAttribute(true)]
  public static bool IsSoundEffects { get; set; } = true;
}

For retrieving the same, you could depend on reflection.

var defaultValue = typeof(SomeClass).GetProperty(nameof(SomeClass.IsSoundEffects), BindingFlags.Public | BindingFlags.Static)
                                .GetCustomAttribute<DefaultValueAttribute>().DefaultValue;

Making the reflection call a generic method to be used with other properties.

public T GetDefaultValue<T>(string propertyName)
{
    var result = typeof(SomeClass).GetProperty(nameof(SomeClass.IsSoundEffects), BindingFlags.Public | BindingFlags.Static)
                                .GetCustomAttribute<DefaultValueAttribute>().DefaultValue;

    return (T)Convert.ChangeType(result,typeof(T));
}

Usage

var defaultValue = GetDefaultValue<bool>(nameof(SomeClass.IsSoundEffects));
like image 21
Anu Viswan Avatar answered Oct 20 '22 15:10

Anu Viswan