C# 在运行时设置常量/枚举?

C# 在运行时设置常量/枚举?,c#,C#,这似乎是一个奇怪的要求,我很感激,但情况就是这样: 我有一个程序,它依赖于读取一些文件。这些文件的名称如下:foo_bar_BAZ.txt,其中BAZ是项目的名称,在运行时之前是未知的。但是,在整个程序执行过程中,它不会改变 我想要一个存储所有文件名的字符串枚举列表。到目前为止,我使用了这样一个密封类: public sealed class SQLFile { private readonly String name; private readonly String value

这似乎是一个奇怪的要求,我很感激,但情况就是这样:

我有一个程序,它依赖于读取一些文件。这些文件的名称如下:
foo_bar_BAZ.txt
,其中BAZ是项目的名称,在运行时之前是未知的。但是,在整个程序执行过程中,它不会改变

我想要一个存储所有文件名的字符串枚举列表。到目前为止,我使用了这样一个密封类:

public sealed class SQLFile
{
    private readonly String name;
    private readonly String value;

    public static readonly SQLFile CrByAuthors = new SQLFile("Changes_CR_By_Authors_%project_name%.txt", "CrByAuthors");
    public static readonly SQLFile DocumentCrMetrics = new SQLFile("Changes_Document_CR_Output_%project_name%.txt", "DocumentCrMetrics");
    [...]


    private SQLFile(String value, String name)
    {
        this.name = name;
        this.value = value;
    }

    public String ToString(string projectName)
    {
        return this.value.Replace("%project_name%", projectName);
    }
}
正如您所看到的,这取决于我每次想要访问文件名时都提供projectname变量,即使该文件名从运行时开始到结束都是常量


有没有更优雅的方法来处理这种情况?

一个简单的解决方案是使用带有
ProjectName
属性的静态类。此属性的值在应用程序启动期间设置。然后,您的类可以使用该属性。

将静态属性添加到
SQLFile
,类似于

public sealed class SQLFile
{  
    //...
    private static string sProjectName;
    public static string ProjectName
    {
        get
        {
            return sProjectName;
        }
        set
        {
            //optionally, you could prevent updates with:
            //if (string.IsNullOrEmpty(sProjectName))
            sProjectName= value;
            //else throw Exception("ProjectName was already set!");
        }
}

[编辑-我读代码有点太快了,所以这就是我真正的意思:

(名称不好的IMHO)方法
ToString
的目的是返回与特定项目名称对应的文件名。这没有什么错,尽管这可能是一种责任,可能属于一个分离的阶级

例如,您可以重构代码以更清楚地表达其意图:

interface ISqlFileNameProvider 
{
     string SqlFilename { get; }
}
然后有一个简单的(“穷人的”)实现:

public class SimpleSqlFileNameProvider : ISqlFileNameProvider
{
    private readonly string _filename;
    public SimpleSqlFileNameProvider(string filename)
    {
        _filename = filename;
    }

    public string SqlFilename
    {
        get { return _filename; }
    }
}
然后从这里派生专门的实现:

public class TemplateSqlFileNameProvider : SimpleSqlFileNameProvider
{
    public TemplateSqlFileNameProvider(string template, string projectName)
        : base(template.Replace("%project_name%", projectName))
    { }
}

public class CrByAuthorsFileNameProvider : TemplateSqlFileNameProvider
{
    public CrByAuthorsFileNameProvider(string projectName)
        : base("Changes_CR_By_Authors_%project_name%.txt", projectName)
    { }
}

public class DocumentCrMetricsFileNameProvider : TemplateSqlFileNameProvider
{
    public DocumentCrMetricsFileNameProvider(string projectName)
        : base("Changes_Document_CR_Output_%project_name%.txt", projectName)
    { }
}
首先,请注意,
projectName
仍然是这些专用类的
构造函数的参数。这里没有globals。接下来,即使您在项目中添加了一些管道代码,也可以更轻松地解耦类以进行更简单的测试:您可以创建
ISqlFileNameProvider
的模拟实现,并返回任何您想要测试的功能,而无需写入实际数据文件


我当然会建议不要使用全球财产。您可以将项目名称指定为构造函数参数,这意味着您可以轻松地测试类的行为是否符合您的要求。即使您认为它会在项目生命周期内发生变化,您也很容易遇到这样一种情况,即您暂时需要在运行时切换项目名称。我建议不要使用globals。

通过反射获取项目名称?为什么不从配置文件获取
projectName
?globals是维护的噩梦,是地狱中最纯粹的恶魔。:)但是,是的,没有什么能比得上一个简单的全局程序。@Groo:这种对一般好建议的不加思考/教条的使用,正是使某些程序本身成为一场噩梦的原因。从开发人员的机制来看,常数与静态属性相同。因为他想要一个常量,我的建议当然不会增加维护问题。如果他在一个使用DI的架构中,我会建议使用一个方法
GetCurrentProjectName
的接口
IProjectNameProvider
。但看起来他并没有使用那种架构,我的问题是,我不同意我们讨论的是常数这一前提(尽管问题标题这么称呼它)。OP的
SQLFile
类的用途非常简单:给定一个项目名称,返回一个特定格式的文件名,该文件名将对应于该项目。将固定依赖项添加到全局属性非常简单,但这会让类对世界其他部分了解得太多。@Groo:我倾向于同意你的最后一句话。但在不了解世界其他地方的情况下,我觉得建议总体建筑改进是一种冒险。我的第一个建议是将项目名称作为构造函数参数传递,但由于未知原因,该构造函数是私有的,因此这似乎不是一个选项。他没有将项目名称指定为构造函数参数。它被传递给
ToString
,因此调用该方法的人需要知道项目名称。如果这不是呼叫者的事,我认为他目前的方法确实有问题。@Daniel:是的,我以为OP是通过ctor设置属性的。糟糕的是,我没有足够清楚地表达我的想法,我会更新我的答案。@Groo如果我理解正确的话,你建议用这个新方法替换我当前的密封类,实际上所有这些类声明都像枚举一样代表访问文件名的方式?我想我需要大量阅读来理解接口和提供者之类的概念,甚至是
base()
所代表的内容