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()
所代表的内容