以实现通用设置类的最佳方式 - 获取/设置属性体现?(Best way to implement g

2019-10-17 14:11发布

我不知道我怎样才能使一个通用的设置类,希望你能帮助我。
首先,我想一个设置文件的解决方案。 为此,我创建了一个单身是这样的:

public sealed class Settings
{
  private static readonly Lazy<Settings> _instance = new Lazy<Settings>(() => new Settings());
  private Dictionary<string, object> m_lProperties = new Dictionary<string, object>();

  public void Load(string fileName)
  {
    throw new NotImplementedException();  
  }

  public void Save(string fileName)
  {
    throw new NotImplementedException();
  }

  public void Update()
  {
    throw new NotImplementedException();
  }

  /// <summary>
  /// Gets the propery.
  /// </summary>
  /// <param name="propertyName">Name of the property.</param>
  /// <returns></returns>
  public string GetPropery(string propertyName)
  {
    return m_lProperties[propertyName].ToString() ?? String.Empty;
  }

  /// <summary>
  /// Gets the propery.
  /// </summary>
  /// <param name="propertyName">Name of the property.</param>
  /// <param name="defaultValue">The default value.</param>
  /// <returns></returns>
  public string GetPropery(string propertyName, string defaultValue)
  {
    if (m_lProperties.ContainsKey(propertyName))
    {
      return m_lProperties[propertyName].ToString();
    }
    else
    {
      SetProperty(propertyName, defaultValue);
      return defaultValue;
    }
  }

  /// <summary>
  /// Sets the property.
  /// </summary>
  /// <param name="propertyName">Name of the property.</param>
  /// <param name="value">The value.</param>
  public void SetProperty(string propertyName, string value)
  {
    if (m_lProperties.ContainsKey(propertyName))
      m_lProperties[propertyName] = value;
    else
      m_lProperties.Add(propertyName, value);
  }
}

但我认为,更好的办法是,属性中的类,我可以通过反射得到的属性。
- 你能帮助我实现这样的事情?
- 是否有可能给性能属性,如“加密=真”? - 请告诉我保存/在XML文件中加载设置的最佳方法是什么?

更新
下面是一个例子,如何使用环境下的实际:

class Test()
{
  private string applicationPath;
  private string configurationPath;
  private string configurationFile;

  public Test()
  {
    applicationPath = Settings.Instance.GetPropery("ApplicationPath", AppDomain.CurrentDomain.BaseDirectory);
    configurationPath = Settings.Instance.GetPropery("ConfigurationPath", "configurations");  
    configurationFile = Settings.Instance.GetPropery("ConfigurationFile", "application.xml");  
    // ... Load file with all settings from all classes
  } 

Answer 1:

这这里是从我自己的代码,而一个相关位。

public class MyObject
{
    public string StringProperty {get; set;}

    public int IntProperty {get; set;}

    public object this[string PropertyName]
        {
            get
            {
                return GetType().GetProperty(PropertyName).GetGetMethod().Invoke(this, null);
            }
            set
            {
                GetType().GetProperty(PropertyName).GetSetMethod().Invoke(this, new object[] {value});
            }
        }
}

它所允许的话,是这样的:

MyObject X = new MyObject();
//Set
X["StringProperty"] = "The Answer Is: ";
X["IntProperty"] = 42;
//Get - Please note that object is the return type, so casting is required
int thingy1 = Convert.ToInt32(X["IntProperty"]);
string thingy2 = X["StringProperty"].ToString();

更新:更多的解释其工作原理是反射性访问属性 ,属性是从该领域的不同,他们使用getter和setter方法,而不是被直接声明和访问。 如果空检查从的getProperty回报,而不是简单地假设它的工作原理,你可以使用相同的方法来获得字段,或者得到场。 此外,作为另一种意见指出,这将如果你调用它是不存在的属性,因为它缺乏任何形式的错误捕获打破。 我发现在最简单的可能形式,而不是它最强大的表单的代码。

至于财产属性....这需要索引要与使用它的类里面创建(或父类,我有我的BaseObject ),所以在内部就可以实现给定的属性的属性,然后应用开关或防止当它们被访问的属性的检查。 也许让所有的属性,你实现一些其他的自定义类Object Value; Bool Encrypted; Object Value; Bool Encrypted; 然后从那里需要进行这项工作,它真的只是取决于你想多么花哨得到你想要多少代码编写。



Answer 2:

我不reccommend的地方也可能没有它使用反射,因为它很慢。

我没有思考和加密原型例如:

public sealed class Settings
{
    private static readonly HashSet<string> _propertiesForEncrypt = new HashSet<string>(new string[] { "StringProperty", "Password" });
    private static readonly Lazy<Settings> _instance = new Lazy<Settings>(() => new Settings());
    private Dictionary<string, object> m_lProperties = new Dictionary<string, object>();

    public void Load(string fileName)
    {
        // TODO: When you deserialize property which contains into "_propertiesForEncrypt" than Decrypt this property.
        throw new NotImplementedException();
    }

    public void Save(string fileName)
    {
        // TODO: When you serialize property which contains into "_propertiesForEncrypt" than Encrypt this property.
        throw new NotImplementedException();
    }

    public void Update()
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// Gets the propery.
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    /// <returns></returns>
    public object GetPropery(string propertyName)
    {
        if (m_lProperties.ContainsKey(propertyName))
            return m_lProperties[propertyName];

        return null;
    }

    /// <summary>
    /// Gets the propery.
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    /// <param name="defaultValue">The default value.</param>
    /// <returns></returns>
    public object GetPropery(string propertyName, object defaultValue)
    {
        if (m_lProperties.ContainsKey(propertyName))
        {
            return m_lProperties[propertyName].ToString();
        }
        else
        {
            SetProperty(propertyName, defaultValue);
            return defaultValue;
        }
    }

    /// <summary>
    /// Sets the property.
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    /// <param name="value">The value.</param>
    public void SetProperty(string propertyName, object value)
    {
        if (m_lProperties.ContainsKey(propertyName))
            m_lProperties[propertyName] = value;
        else
            m_lProperties.Add(propertyName, value);
    }


    // Sample of string property
    public string StringProperty
    {
        get
        {
            return GetPropery("StringProperty") as string;
        }
        set
        {
            SetProperty("StringProperty", value);
        }
    }

    // Sample of int property
    public int IntProperty
    {
        get
        {
            object intValue = GetPropery("IntProperty");
            if (intValue == null)
                return 0; // Default value for this property.

            return (int)intValue;
        }
        set
        {
            SetProperty("IntProperty", value);
        }
    }
}


Answer 3:

使用动态类是这样的: https://gist.github.com/3914644 yourObject.stringProperty或yourObject.intProperty:所以你可以为访问您的属性



Answer 4:

其中一个最大的问题是,有没有干净的方式来反序列化对象为对象。 如果你不提前知道什么时间该对象的类型必须是,它非常努力地工作着。 因此,我们有一个替代的解决方案,存储类型的信息。

鉴于其没有上市,我会提供什么样的,我认为一个示例XML,以及使用它的方法和访问属性本身的方法。 您正在使用get和set属性的功能是为是功能性的,并要求没有变化。

在单独的类,你需要确保在类引用相关属性设置类在自己的get / set方法

public int? MyClassProperty
{
    get
    {
        return (int?)Settings.Instance.GetProperty("MyClassProperty");
    }
    set
    {
        Settings.Instance.SetProperty("MyClassProperty", value);
    }
}

在您加载和保存功能,你将要使用序列化,具体而言, XmlSerializer 。 要做到这一点,你需要适当声明你的设置列表。 为此,我将实际使用自定义类。

更新,以便正确装载

public class AppSetting
{
    [XmlAttribute("Name")]
    public string Name { get; set; }
    [XmlAttribute("pType")]
    public string pType{ get; set; }
    [XmlIgnore()]
    public object Value{ get; set; }
    [XmlText()]
    public string AttributeValue 
    {
        get { return Value.ToString(); }
        set {
        //this is where you have to have a MESSY type switch
        switch(pType) 
        { case "System.String": Value = value; break;
          //not showing the whole thing, you get the idea
        }
    }
}

然后,而不仅仅是一本字典,你会是这样的:

public sealed class Settings
{
  private static readonly Lazy<Settings> _instance = new Lazy<Settings>(() => new Settings());
  private Dictionary<string, object> m_lProperties = new Dictionary<string, object>();
  private List<AppSetting> mySettings = new List<AppSetting>();

您的负载功能将是一个简单的反序列化

public void Load(string fileName)
{//Note: the assumption is that the app settings XML will be defined BEFORE this is called, and be under the same name every time.
    XmlSerializer ser = new XmlSerializer(typeof(List<AppSetting>));
    FileStream fs = File.Open(fileName);
    StreamReader sr = new StreamReader(fs);
    mySettings = (List<AppSetting>)ser.DeSerialize(sr);
    sr.Close();
    fs.Close();

    //skipping the foreach loop that will add all the properties to the dictionary
}

保存功能将基本上需要扭转它。

public void Save(string fileName)
    {
        //skipping the foreach loop that re-builds the List from the Dictionary
        //Note: make sure when each AppSetting is created, you also set the pType field...use Value.GetType().ToString()

        XmlSerializer ser = new XmlSerializer(typeof(List<AppSetting>));
        FileStream fs = File.Open(fileName, FileMode.Create);
        StreamWriter sw = new StreamWriter(fs);
        //get rid of those pesky default namespaces
        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
        ns.Add("", "");
        ser.Serialize(sw, mySettings, ns);
        sw.Flush();
        sw.Close();
        fs.Close();
        mySettings = null;//no need to keep it around
    }

和XML将类似于这样的事情:

更新

<ArrayOfAppSetting>
    <AppSetting Name="ApplicationPath" pType="System.String">C:\Users\ME\Documents\Visual Studio 2010\Projects\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug\</AppSetting> 
    <AppSetting Name="ConfigurationPath" pType="System.String">configurations</AppSetting> 
    <AppSetting Name="ConfigurationFile" pType="System.String">application.xml</AppSetting> 
    <AppSetting Name="prop" pType="System.Int32">1</AppSetting> 
</ArrayOfAppSetting>

我发现使用中间这个例子中List<>因为事实证明,你不能使用任何与XmlSerializer的实现IDictionary中。 这将无法完成初始化,这是行不通的。

您可以沿着字典创建和维护该列表,也可以更换名单的字典...请确保您有检查,以验证“名称”是唯一的,或者你可以简单地忽略列表中除了保存期间加载操作(这是我写的怎么这个例子)

更新这真的只能用原始类型效果很好 (INT,双,字符串,等等),而是因为你直接存储类型,你可以使用任何你想要的自定义类型,因为你知道它是什么,用它做,你只需要处理它的AttributeValue的设置方法

另一个更新:如果您只存储而不是所有类型的对象的字符串,...这很可笑的简单。 摆脱了XmlIgnore valuepType ,然后自动实现AttributeValue 。 热潮,完成。 这会限制你的字符串和其他原语虽然,确保了get / set在适当投他们的其他类值...但它是一个更简单,更容易实现。



文章来源: Best way to implement generic setting class - Get/Set Properties reflected?