如何在更改后获取自动实现的默认编译时值 属性 C# 6.0?

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

很快,新的 C# 6.0 自动实现 属性 让我们可以做到这一点

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

现在在某个地方,我更改了属性 IsSoundEffects = false,所以访问它会是错误的。

嗯,那么如何获得真正的默认编译时自动实现的 属性 值。

类似于:
Type.GetPropertyDefaultValue(IsSoundEffects); // 一个真正的编译时 = 真

default(IsSoundEffects)   // idk, something like that

Why I need that?

因为我从数据库中填充属性。如果用户需要恢复默认值,则恢复它。例如设置。

看起来很奇怪?我搜索够了,但是所有关于自动实现功能的例子都没有恢复默认值。

已编辑

提供的最佳方法

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

Christopher answers for constants as default values.

属性只是 get/set 函数对的语法糖。当构造函数运行时,你得到的只是一个基本的、标准的 assignment/function 调用。作为所有文字和常量,它在运行时不应再存在

天真的方法是使用一个像 IsSoundEffectsDefaultValue 这样的常量。而且我确实认为在大多数情况下这就足够了。

我的一个旧想法可能适用于您的更广泛的问题。但是我需要搜索代码。

编辑:

不幸的是,我找不到我的旧代码。但是我可以重新创建它。基本思想是拥有多个 "layers" 值,其中一个值隐藏(但不覆盖)另一个值。

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

编辑 2:

WPF/MVVM圈子里可能会有更好的解决方案。许多 MVVM 正在使用相同的代码编写属性 - 一个通过 INotifyPropertyChanged 引发更改通知的代码。属性的一个大问题不是偶然写入支持字段。

有些人找到了解决方案,例如将实际值和所有代码自动放入 Dictionar<string, object> 之类的东西中。但是,我不确定细节。 DependancyProperties 也可能接近它。

您可以使用的一种方法是基于自定义属性。您可以定义为自定义属性以保留默认值。例如,

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

您现在可以使用属性将默认值存储为

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

要检索相同的内容,您可以依赖于反射。

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

使反射调用成为与其他属性一起使用的通用方法。

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

用法

var defaultValue = GetDefaultValue<bool>(nameof(SomeClass.IsSoundEffects));

使用PropertyInfo.GetConstantValue Method.

考虑一下您的 class

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

并考虑 "getter" class 您创建的

class DefaultGetter
{
    T GetDefault<TClass, TProp>(string propertyName) where T : new()
    {        
        if (!defaults.TryGetValue(typeof(T), out var defaultValues)
            if (!defaultValues.TryGetValue(propertyName, out var value))
            {
                 // reflect on prototype getting all the PropertyInfo
                 // and store them in the dictionary of dictionaries
                 // then assign to value...
                 defaults[typeof(TClass)] = defaultValues = typeof(TClass).GetProperties().Select(x => new { x.Name, Value = x.GetConstantValue() }).ToDictionary(x => x.Name);
            }

            value = defaultValues[propertyName]
        }

        return (TProp)value;
    }

    IDictionary<Type, IDictionary<string, object> defaults = new ... // elided for brevity
}

你会像这样使用它:

var defaultIsSoundEffects = defaultGetter.GetDefault<YourClass, bool>("IsSoundEffects");
  1. 例如属性,新建一个实例然后获取默认值属性是最简单的方法。
  2. 对于静态属性,可以在静态构造函数中保留默认值
    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
        }
    }



问题作者添加的以下行:

用于设置 属性 为默认值

private static void ResetPropertyValue(string PropertyName)
{ 
    typeof(Options).GetProperty(PropertyName).SetValue(null, 
    defaultValues[PropertyName]);
}

您可以使用以下方法

  1. 如果初始化后不需要更改变量,则使用常量很好。
  2. 如果构造函数中只能更改变量,则使用静态只读。
  3. 使用字典

OP: I filling the properties from the database. and restore it if user need to restore the default values.

This post tries to answer based on based on above requirement which you mentioned in the question.(Not based on the question title.)

选项 1 - 使用 System.ComponentModel.DefaultValue

设置属性默认值的标准解决方案之一是在 .NET Framework 中的许多 classes 中使用 DefaultValue attribute of the .NET Framework. You can see 这种模式。例如:

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

然后 .NET Framework 通过 PropertyDescriptor.ResetValue 方法为您提供重置功能:

System.ComponentModel.TypeDescriptor.GetProperties(this)["MyProperty"].ResetValue(this);

选项 2 - 创建静态默认实例

您可以拥有 class 的静态默认实例并将其用于重置属性。

如果您想将默认实例设置为 private,您可以将其保留为单个实例字段。

如果您希望默认实例为 public,您需要在 getter 中创建 属性 和 return 新实例。

public class MyClass
{
    private static readonly MyClass Default = new MyClass();
    //OR
    //public static MyClass Default { get { return new MyClass(); } }

    public bool MyProperty{ get; set; } = true;
    public void Reset()
    {
        this.MyProperty = Default.MyProperty;
    }
}