如何在C#中封装“全局”变量?/最佳实践


9

在C#中,封装需要在多种方法中使用的变量的最佳实践是什么?可以简单地在这两个方法上方的类顶部声明它们吗?

另外,如果我从配置文件中使用应用程序设置,是否应该使用吸气剂?像这样...

private string mySetting{ get { return WebConfigurationManager.AppSettings["mySetting"]; } }

什么是最佳做法?


除了添加一个额外的(可能是不必要的)间接层之外,getter的目的是什么?
罗伯特·哈维

4
吸气剂比多次调用要好得多,WebConfigurationManager.AppSettings因为稍后更改将更容易
Daniel Little

@Lavinski:当然,如果您认为以后可以将数据存储换成另一个存储。实际上,这种情况很少发生,并且对于AppSettings发生的可能性似乎很小。
罗伯特·哈维

10
“ getter”的优点是可以使智能感知工作-并且只在一个位置具有密钥字符串“ mySetting”(如果编译器正确编写,则不会检查它)。
Doc Brown

Answers:


5

不仅OK。根据《清洁代码》一书,这实际上是一个很好的做法,鲍勃叔叔真的鼓励这样做。许多方法使用的变量可能显示方法之间的高度内聚性。此外,高度的对象变量还可能暗示应将所述类分为两部分,因此将其声明为对象变量可以帮助您找到隐藏的类候选对象。

对象级变量不是全局变量,因此如果应该由各种方法共享它们,不要害怕使用它们。


感谢您的帮助,尽管我认为当您说凝聚力时,您的意思确实是耦合。
user1944367 2013年

不,我的意思是凝聚力。在软件工程课上,我也很难理解对高凝聚力的渴望。通常我们渴望低耦合和高凝聚力。耦合是我们可以通过自己的方法看到的物理事物。如果一个类使用另一个类,则将其与之耦合。如果实际上实例化了该类的对象,那么它就非常好了。但是,凝聚力更合乎逻辑。类中的高内聚性意味着,即使它们之间不共享任何变量,其方法也属于非常相似的域。
Uri 2013年

使用对象变量的各种方法不一定意味着它们耦合在一起。我可以使用一个带有char []密码变量的Encrypter类,并使用Encrypt(string text); 和解密(字符串文本);里面的方法。他们两个都使用相同的密码变量,但是它们之间没有明显的耦合。但是,您可能会注意到,它们处理同一个域,即文本加密。据我所知,它们具有高度的凝聚力,尽管上述课程可以分为两部分。有人可能会认为加密不属于解密领域。
Uri 2013年

4

不断封装设置是一个好主意。

我要做的是创建一个设置类,该类可以是一个静态全局一个或多个实例类,然后将通过依赖项注入进行管理。然后,在启动时,我将从配置中加载所有设置到该类中。

我还编写了一个小库,该库利用反射使此过程变得更加容易。

一旦我的设置在我的配置文件中

<?xml version="1.0" encoding="utf-8" ?>
<configuration>   
    <appSettings>
        <add key="Domain" value="example.com" />
        <add key="PagingSize" value="30" />
        <add key="Invalid.C#.Identifier" value="test" />
    </appSettings>
</configuration>

我根据需要制作一个静态或实例类。对于只有几个设置的简单应用程序,一个静态类就可以了。

private static class Settings
{
    public string Domain { get; set; }

    public int PagingSize { get; set; }

    [Named("Invalid.C#.Identifier")]
    public string ICID { get; set; }

}

然后使用我的库调用Inflate.Static或者Inflate.Instance,很酷的事情是我可以使用任何键值源。

using Fire.Configuration;

Inflate.Static( typeof(Settings), x => ConfigurationManager.AppSettings[x] );

所有的代码都在GitHub上的https://github.com/Enexure/Enexure.Fire.Configuration

甚至还有一个nuget包:

PM>安装包Enexure.Fire.Configuration

参考代码:

using System;
using System.Linq;
using System.Reflection;
using Fire.Extensions;

namespace Fire.Configuration
{
    public static class Inflate
    {
        public static void Static( Type type, Func<string, string> dictionary )
        {
            Fill( null, type, dictionary );
        }

        public static void Instance( object instance, Func<string, string> dictionary )
        {
            Fill( instance, instance.GetType(), dictionary );
        }


        private static void Fill( object instance, Type type, Func<string, string> dictionary ) 
        {

            PropertyInfo[] properties;
            if (instance == null) {

                // Static
                properties = type.GetProperties( BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly );
            } else {

                // Instance
                properties = type.GetProperties( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly );
            }

            // Get app settings and convert
            foreach (PropertyInfo property in properties) {
                var attributes = property.GetCustomAttributes( true );
                if (!attributes.Any( x => x is Ignore )) {

                    var named = attributes.FirstOrDefault( x => x is Named ) as Named;

                    var value = dictionary((named != null)? named.Name : property.Name);

                    object result;
                    if (ExtendConversion.ConvertTo(value, property.PropertyType, out result)) {
                        property.SetValue( instance, result, null );
                    }
                }
            }
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.