是否可以简化此 C# 代码,使我的方法不超过 500 行并且更易于维护?
Can this C# code be simplified so that my method is not over 500 lines long and easier to maintain?
我有一副光秃秃的骨头class:
internal class CLMExplorerTranslations
{
// LBL_BROTHER
public string Brother { get; set; }
// LBL_SISTER
public string Sister { get; set; }
// LBL_ABBREV_CHAIR
public string Chairman { get; set; }
// LBL_ABBREV_TREAS_TALK
public string TreasuresTalk { get; set; }
// LBL_ABBREV_TREAS_DIG
public string SpiritualGems { get; set; }
// LBL_ABBREV_TREAS_READ
public string BibleReading { get; set; }
// LBL_TALK
public string Talk { get; set; }
// LBL_ABBREV_DEMO
public string Demonstration { get; set; }
// LBL_ABBREV_ASST
public string Assistant { get; set; }
// LBL_ABBREV_LIVING
public string Living { get; set; }
// LBL_ABBREV_CBS
public string ConductorCBS { get; set; }
// LBL_ASSIGNMENT_CBS_READ
public string ReaderCBS { get; set; }
// LBL_ABBREV_PRAY
public string Prayer { get; set; }
public CLMExplorerTranslations()
{
Brother = "Brother";
Sister = "Sister";
Chairman = "Chair";
TreasuresTalk = "Treas. Talk";
SpiritualGems = "Spiritual Gems";
BibleReading = "Bible Read";
Talk = "Talk";
Demonstration = "Demos";
Assistant = "Asst";
Living = "Living";
ConductorCBS = "CBS";
ReaderCBS = "CBS Reader";
Prayer = "Pray";
}
}
如您所见,非常简单。构造函数使用英语值初始化属性。然后我有一个 public 方法,该方法将语言代码作为字符串传递。这反过来会更新属性。例如:
public void InitTranslations(string langCode)
{
if (langCode == "AFK")
{
Brother = "Broer";
Sister = "Suster";
Chairman = "Voors.";
TreasuresTalk = "Skatte toespr.";
SpiritualGems = "Skatte soek";
BibleReading = "Skatte leesged.";
Talk = "Toespraak";
Demonstration = "Demon.";
Assistant = "Asst";
Living = "Lewe";
ConductorCBS = "GBS";
ReaderCBS = "GBS leser";
Prayer = "Geb.";
return;
}
if (langCode == "CHS")
{
Brother = "弟兄";
Sister = "姊妹";
Chairman = "主席";
TreasuresTalk = "宝藏";
SpiritualGems = "挖掘";
BibleReading = "朗读";
Talk = "演讲";
Demonstration = "示范";
Assistant = "助手";
Living = "生活";
ConductorCBS = "研经班";
ReaderCBS = "课文朗读者";
Prayer = "祷告";
return;
}
// More
}
总共有 26 个 if
从句用于 26 种不同的语言。翻译设置一次,然后不需要再次更改。
它运行良好,但是否有更简单的方法来管理它,并且不会以大约 500 行长的函数结束?
此 class 是 DLL 库的一部分/
上下文
它不适用于 GUI。我的 DLL 的一部分正在使用 CvsHelper
读取 CSV 文档。 CSV 中的一个字段有多个值,它们都有自己的分隔符。我将这个单个字段拆分为一个值列表,然后需要解析这些值以识别每个值是什么。用户声明 CSV 文件的语言,以便我知道要测试的值。然后,当我找到匹配项时,我可以将其转换为我自己的枚举值,供我自己的 classes 使用。
评论中有人建议我使用嵌入式资源。但鉴于上述情况,尚不清楚如何做到这一点。例如。如果我将 CHS
传递给函数,那么它需要检索 CHS
嵌入的资源值。
我看到刚刚添加了更多评论供我查看。
更新
根据其中一个答案,我正在尝试添加单个 JSON 文件。以为我会从英语开始。我尝试向我的 DLL 添加一个函数来获取翻译:
private CLMExplorerTranslations GetTranslations(string languageCode)
{
using var stream = typeof(MSAToolsLibraryClass).Assembly.GetManifestResourceStream(
$"MSAToolsLibrary.Resources.language-{languageCode}.json");
使用 var reader = new StreamReader(stream, Encoding.UTF8);
return JsonSerializer.Deserialize(reader.ReadToEnd());
}
但这给了我两个问题:
- 错误 CS8370:功能 'using declarations' 在 C# 7.3 中不可用。请使用 8.0 或更高版本的语言。
- 错误 CS1503:参数 1:无法从字符串转换为 Newtonsoft.Json.JsonReader。
更新 2
通过调整代码对第一个错误进行排序:
private CLMExplorerTranslations GetTranslations(string languageCode)
{
using (var stream = typeof(MSAToolsLibraryClass).Assembly.GetManifestResourceStream($"MSAToolsLibrary.Resources.language-{languageCode}.json"))
using (var reader = new StreamReader(stream, Encoding.UTF8))
return JsonSerializer.Deserialize<CLMExplorerTranslations>(reader.ReadToEnd());
}
但是这行仍然有第二个错误:
JsonSerializer.Deserialize<CLMExplorerTranslations>(reader.ReadToEnd());
更新 3
我通过使用以下方式获得它来处理单个 JSOn 文件:
private CLMExplorerTranslations GetTranslations(string languageCode)
{
using (var stream = typeof(MSAToolsLibraryClass).Assembly.GetManifestResourceStream(
$"MSAToolsLibrary.Resources.language-{languageCode}.json"))
using (var reader = new StreamReader(stream, Encoding.UTF8))
return JsonConvert.DeserializeObject<CLMExplorerTranslations>(reader.ReadToEnd());
//return JsonSerializer.Deserialize<CLMExplorerTranslations>(reader.ReadToEnd());
}
可以添加26个json文件,然后按命名规则加载(即:us.json/chs.json/afk.json)
然后你可以像这样创建和初始化(在代码中使用Newtonsoft.Json)
public static CLMExplorerTranslations Load(string langCode)
{
return JsonConvert.DeserializeObject<CLMExplorerTranslations>(File.ReadAllText($"{langCode.ToLowerInvariant()}.json"));
}
鉴于您现在拥有的代码很容易复制粘贴并创建 json 文件,其中包含一些 search/replace 在记事本++中
我会使用资源文件,下面是您的设置方式:
在您的项目中,为您的资源文件创建一个文件夹
在此文件夹中,为您要支持的每种语言添加一个JSON文件,内容如
{
"Brother": "Broer",
"Sister": "Suster",
...
}
根据需要使用所有语言代码将它们命名为 language-AFK.json
。
在您的项目中,右键单击每个文件并转到属性并将构建操作设置为“EmbeddedResource”
注意! 重要的是不要忘记对一个文件执行此操作,因为这会在编译时将文件留在磁盘上并且它不会成为输出组件。 (请参阅下面的额外提示,以确保对未来的语言文件也这样做)
然后在某处添加这样的代码:
using var stream = typeof(SomeClass).Assembly.GetManifestResourceStream(
$"Namespace.To.Your.Folder.language-{languageCode}.json");
using var reader = new StreamReader(stream, Encoding.UTF8);
return JsonSerializer.Deserialize<CLMExplorerTranslations>(reader.ReadToEnd());
注意您不应该使用点 .
来分隔您的资源文件前缀,例如“语言”与语言代码,因为这实际上只会保留由于资源命名约定的使用方式,其中一个文件。相反,我在上面使用了减号 -
。
提示 如果您似乎无法正确命名资源文件,就像您仔细检查所有内容并收到流 null
的错误类似地,您可以 运行 编写这样的代码来检查您的资源 实际上 命名,然后相应地进行调整:
foreach (string name in typeof(SomeClass).Assembly.GetManifestResourceNames())
Console.WriteLine($"resource: {name}");
奖金提示:
- 您现在甚至可以添加单元测试来验证没有 JSON 文件缺少密钥或有额外的密钥,以确保您始终翻译所有内容(我称这些质量测试为,尽管它们使用的是单元测试框架)
- 您还可以使用质量测试来确保磁盘上该文件夹中的文件确实作为嵌入资源存在于您正在测试的程序集中,以确保您永远不会忘记嵌入其中一个文件,例如,如果您添加未来一个新的
- 如果资源文件很大,你也可以压缩它们,虽然这将需要你在构建时做一些额外的跑腿工作,通常这是不值得的,但至少它是一个选择
使用单个 CSV 配置文件
这不一定是最优雅的方法,但却是一种非常实用的方法。
将语言代码 属性 添加到您的 class
internal class CLMExplorerTranslations
{
public string LangCode { get; set: }
//etc
}
创建一个 Excel 电子表格,每种语言一行,每种 属性(包括 LangCode
)一列。确保包含 header 行和 属性 个名称。
将电子表格另存为 CSV
修改代码以使用 GetRecords 导入电子表格(因为无论如何您都在使用 CsvHelper)。
CLMExplorerTranslations GetTranslation(string langCode)
{
using (var reader = new StreamReader("ColumnDefinitions.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = csv.GetRecords<CLMExplorerTranslations>();
var translation = records.SingleOrDefault( x => x.LangCode == langCode );
if (translation == null) throw ArgumentException("Invalid language code");
return translation;
}
}
我认为您可以看到这只是非常少量的工作,没有引入任何新的依赖项(您已经在使用 CsvHelper
),并且具有能够添加和在不接触代码的情况下修改语言。我个人认为编辑电子表格比编辑一系列资源要容易得多。
我有一副光秃秃的骨头class:
internal class CLMExplorerTranslations
{
// LBL_BROTHER
public string Brother { get; set; }
// LBL_SISTER
public string Sister { get; set; }
// LBL_ABBREV_CHAIR
public string Chairman { get; set; }
// LBL_ABBREV_TREAS_TALK
public string TreasuresTalk { get; set; }
// LBL_ABBREV_TREAS_DIG
public string SpiritualGems { get; set; }
// LBL_ABBREV_TREAS_READ
public string BibleReading { get; set; }
// LBL_TALK
public string Talk { get; set; }
// LBL_ABBREV_DEMO
public string Demonstration { get; set; }
// LBL_ABBREV_ASST
public string Assistant { get; set; }
// LBL_ABBREV_LIVING
public string Living { get; set; }
// LBL_ABBREV_CBS
public string ConductorCBS { get; set; }
// LBL_ASSIGNMENT_CBS_READ
public string ReaderCBS { get; set; }
// LBL_ABBREV_PRAY
public string Prayer { get; set; }
public CLMExplorerTranslations()
{
Brother = "Brother";
Sister = "Sister";
Chairman = "Chair";
TreasuresTalk = "Treas. Talk";
SpiritualGems = "Spiritual Gems";
BibleReading = "Bible Read";
Talk = "Talk";
Demonstration = "Demos";
Assistant = "Asst";
Living = "Living";
ConductorCBS = "CBS";
ReaderCBS = "CBS Reader";
Prayer = "Pray";
}
}
如您所见,非常简单。构造函数使用英语值初始化属性。然后我有一个 public 方法,该方法将语言代码作为字符串传递。这反过来会更新属性。例如:
public void InitTranslations(string langCode)
{
if (langCode == "AFK")
{
Brother = "Broer";
Sister = "Suster";
Chairman = "Voors.";
TreasuresTalk = "Skatte toespr.";
SpiritualGems = "Skatte soek";
BibleReading = "Skatte leesged.";
Talk = "Toespraak";
Demonstration = "Demon.";
Assistant = "Asst";
Living = "Lewe";
ConductorCBS = "GBS";
ReaderCBS = "GBS leser";
Prayer = "Geb.";
return;
}
if (langCode == "CHS")
{
Brother = "弟兄";
Sister = "姊妹";
Chairman = "主席";
TreasuresTalk = "宝藏";
SpiritualGems = "挖掘";
BibleReading = "朗读";
Talk = "演讲";
Demonstration = "示范";
Assistant = "助手";
Living = "生活";
ConductorCBS = "研经班";
ReaderCBS = "课文朗读者";
Prayer = "祷告";
return;
}
// More
}
总共有 26 个 if
从句用于 26 种不同的语言。翻译设置一次,然后不需要再次更改。
它运行良好,但是否有更简单的方法来管理它,并且不会以大约 500 行长的函数结束?
此 class 是 DLL 库的一部分/
上下文
它不适用于 GUI。我的 DLL 的一部分正在使用 CvsHelper
读取 CSV 文档。 CSV 中的一个字段有多个值,它们都有自己的分隔符。我将这个单个字段拆分为一个值列表,然后需要解析这些值以识别每个值是什么。用户声明 CSV 文件的语言,以便我知道要测试的值。然后,当我找到匹配项时,我可以将其转换为我自己的枚举值,供我自己的 classes 使用。
评论中有人建议我使用嵌入式资源。但鉴于上述情况,尚不清楚如何做到这一点。例如。如果我将 CHS
传递给函数,那么它需要检索 CHS
嵌入的资源值。
我看到刚刚添加了更多评论供我查看。
更新
根据其中一个答案,我正在尝试添加单个 JSON 文件。以为我会从英语开始。我尝试向我的 DLL 添加一个函数来获取翻译:
private CLMExplorerTranslations GetTranslations(string languageCode)
{
using var stream = typeof(MSAToolsLibraryClass).Assembly.GetManifestResourceStream(
$"MSAToolsLibrary.Resources.language-{languageCode}.json"); 使用 var reader = new StreamReader(stream, Encoding.UTF8); return JsonSerializer.Deserialize(reader.ReadToEnd()); }
但这给了我两个问题:
- 错误 CS8370:功能 'using declarations' 在 C# 7.3 中不可用。请使用 8.0 或更高版本的语言。
- 错误 CS1503:参数 1:无法从字符串转换为 Newtonsoft.Json.JsonReader。
更新 2
通过调整代码对第一个错误进行排序:
private CLMExplorerTranslations GetTranslations(string languageCode)
{
using (var stream = typeof(MSAToolsLibraryClass).Assembly.GetManifestResourceStream($"MSAToolsLibrary.Resources.language-{languageCode}.json"))
using (var reader = new StreamReader(stream, Encoding.UTF8))
return JsonSerializer.Deserialize<CLMExplorerTranslations>(reader.ReadToEnd());
}
但是这行仍然有第二个错误:
JsonSerializer.Deserialize<CLMExplorerTranslations>(reader.ReadToEnd());
更新 3
我通过使用以下方式获得它来处理单个 JSOn 文件:
private CLMExplorerTranslations GetTranslations(string languageCode)
{
using (var stream = typeof(MSAToolsLibraryClass).Assembly.GetManifestResourceStream(
$"MSAToolsLibrary.Resources.language-{languageCode}.json"))
using (var reader = new StreamReader(stream, Encoding.UTF8))
return JsonConvert.DeserializeObject<CLMExplorerTranslations>(reader.ReadToEnd());
//return JsonSerializer.Deserialize<CLMExplorerTranslations>(reader.ReadToEnd());
}
可以添加26个json文件,然后按命名规则加载(即:us.json/chs.json/afk.json) 然后你可以像这样创建和初始化(在代码中使用Newtonsoft.Json)
public static CLMExplorerTranslations Load(string langCode)
{
return JsonConvert.DeserializeObject<CLMExplorerTranslations>(File.ReadAllText($"{langCode.ToLowerInvariant()}.json"));
}
鉴于您现在拥有的代码很容易复制粘贴并创建 json 文件,其中包含一些 search/replace 在记事本++中
我会使用资源文件,下面是您的设置方式:
在您的项目中,为您的资源文件创建一个文件夹
在此文件夹中,为您要支持的每种语言添加一个JSON文件,内容如
{ "Brother": "Broer", "Sister": "Suster", ... }
根据需要使用所有语言代码将它们命名为
language-AFK.json
。在您的项目中,右键单击每个文件并转到属性并将构建操作设置为“EmbeddedResource”
注意! 重要的是不要忘记对一个文件执行此操作,因为这会在编译时将文件留在磁盘上并且它不会成为输出组件。 (请参阅下面的额外提示,以确保对未来的语言文件也这样做)
然后在某处添加这样的代码:
using var stream = typeof(SomeClass).Assembly.GetManifestResourceStream( $"Namespace.To.Your.Folder.language-{languageCode}.json"); using var reader = new StreamReader(stream, Encoding.UTF8); return JsonSerializer.Deserialize<CLMExplorerTranslations>(reader.ReadToEnd());
注意您不应该使用点 .
来分隔您的资源文件前缀,例如“语言”与语言代码,因为这实际上只会保留由于资源命名约定的使用方式,其中一个文件。相反,我在上面使用了减号 -
。
提示 如果您似乎无法正确命名资源文件,就像您仔细检查所有内容并收到流 null
的错误类似地,您可以 运行 编写这样的代码来检查您的资源 实际上 命名,然后相应地进行调整:
foreach (string name in typeof(SomeClass).Assembly.GetManifestResourceNames())
Console.WriteLine($"resource: {name}");
奖金提示:
- 您现在甚至可以添加单元测试来验证没有 JSON 文件缺少密钥或有额外的密钥,以确保您始终翻译所有内容(我称这些质量测试为,尽管它们使用的是单元测试框架)
- 您还可以使用质量测试来确保磁盘上该文件夹中的文件确实作为嵌入资源存在于您正在测试的程序集中,以确保您永远不会忘记嵌入其中一个文件,例如,如果您添加未来一个新的
- 如果资源文件很大,你也可以压缩它们,虽然这将需要你在构建时做一些额外的跑腿工作,通常这是不值得的,但至少它是一个选择
使用单个 CSV 配置文件
这不一定是最优雅的方法,但却是一种非常实用的方法。
将语言代码 属性 添加到您的 class
internal class CLMExplorerTranslations { public string LangCode { get; set: } //etc }
创建一个 Excel 电子表格,每种语言一行,每种 属性(包括
LangCode
)一列。确保包含 header 行和 属性 个名称。将电子表格另存为 CSV
修改代码以使用 GetRecords 导入电子表格(因为无论如何您都在使用 CsvHelper)。
CLMExplorerTranslations GetTranslation(string langCode) { using (var reader = new StreamReader("ColumnDefinitions.csv")) using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) { var records = csv.GetRecords<CLMExplorerTranslations>(); var translation = records.SingleOrDefault( x => x.LangCode == langCode ); if (translation == null) throw ArgumentException("Invalid language code"); return translation; } }
我认为您可以看到这只是非常少量的工作,没有引入任何新的依赖项(您已经在使用 CsvHelper
),并且具有能够添加和在不接触代码的情况下修改语言。我个人认为编辑电子表格比编辑一系列资源要容易得多。