IRONSOFTWAREHOME

C#接口:理解奖品表单接线(Tim Corey,第09课)

C# 应用程序从头到尾课 09 – 奖品窗体连接

Tim Corey

1h 27m 22s

在 Tim Corey 的《C# 应用程序从头到尾》系列中,第 09 课重点是连接奖品窗体。 表面上看,这个窗体似乎简单——只需收集用户输入,验证,创建模型并保存。 但 Tim 解释真正的复杂性在于决定在哪里保存数据:数据库、文本文件还是两者兼有。 Tim 的视频通过介绍 C# 编程中的核心概念:接口,带我们走过解决方案。

在本文中,我们将通过 Tim 的讲解深入研究接口,以便您更好地理解它们如何帮助构建可扩展的、可维护的应用程序。

问题:我们在哪里保存数据?

Tim 开始陈述奖品窗体的目的:它接受输入,验证并将其保存到存储中。 但他警告说,棘手的部分是决定在哪里存储数据。 他强调教程往往跳过这一点,因为这并不容易,但他希望学习者正面解决这一问题。

他解释说,最初您可能会尝试一个简单的解决方案:检查您是使用 SQL 还是文本文件,然后执行正确的保存过程。 但 Tim 很快展示这样做如何变得丑陋且难以维护。 如果每个窗体都必须检查使用哪种存储类型,代码会变得重复、杂乱且难以更改。

丑陋的方式:硬编码条件

Tim 勾画了一个伪代码示例。 他解释可能会开始检查一个布尔值,如 usingSQL == true,然后打开数据库连接,保存模型,并返回带有 ID。 然后您可能对文本文件做相同的操作,手动生成一个 ID,因为文本文件无法自动生成。

他说这很快变得重复。多个窗体需要此逻辑,每次您添加新数据源如 MySQL 时,都必须更新每个窗体。 Tim 称这"不可扩展"并强调它违反了"DRY"原则(不要重复自己)。 他明确表示:"一定有更好的方法。"

拉动线程:更好的方法

Tim介绍了他的策略:拉动线程。 他从询问代码需要哪些信息以及信息来自哪里开始。 他识别出了两个关键问题:

我们如何知道使用哪个数据源?

我们如何连接两个不同的数据源以完成相同的任务?

Tim解释说,唯一不同的是保存的实际操作。 从表单的角度来看,它只需要说:"这是模型。 保存它。"表单不应该关心是保存到SQL还是文本文件。

解决方案:全局配置 + 接口

Tim建议使用全局配置系统。 他说,为了知道要使用哪个数据源,应用程序需要全球可访问的数据,并建议使用静态类来存储此信息。 他承认通常避免使用全局变量,但在这种情况下,确实需要全局数据。

接下来,Tim解释了关键概念:接口。 他将接口定义为合约——任何实现它的类将包含某些方法或属性的承诺。 Tim强调,这使应用程序能够调用相同的方法,而不论数据源。 表单不在乎是SQL还是文本文件;它只关心调用该方法。

Tim说:"如果您需要执行相同的任务,但在幕后是通过两种不同方式完成,您需要一个接口。"

创建接口

Tim通过在Tracker库中创建一个接口来进入实际实现。 他将其命名为IDataConnection,并解释了以"I"开头的接口命名惯例。 他强调这对于清晰地将其识别为接口很重要。

Tim在接口中添加了一个方法:

PrizeModel CreatePrize(PrizeModel model);
Text

他解释说,这个方法是一个合同:它必须存在于任何实现IDataConnection的类中。 表单将调用此方法,并期望返回一个带有ID的PrizeModel。 Tim解释这是表单如何对存储类型保持无偏见的方法。

创建全局配置静态类

接下来,Tim创建了一个名为GlobalConfig的静态类。他解释说,静态类不能被实例化,并且是全球可访问的。 这是应用程序将存储可用数据连接列表的地方。

他定义了一个属性:

public static List<IDataConnection> Connections { get; private set; }
Text

Tim解释使用私有设置,以便只有类本身可以修改列表,而应用程序的其他部分只能读取。

然后他创建了方法:

public static void InitializeConnections(bool database, bool textFiles)
Text

此方法设置可用的数据连接。 Tim强调列表允许多个连接,这意味着应用程序可以保存到SQL、文本文件或两者。

理解接口:一个现实世界的例子

Tim停下来安抚学习者,这是复杂的材料,但可以实现。 他建议先看一次视频,然后边看边编码。

他解释说,接口是一种合同,任何实现它的类都必须遵循合同。 他通过创建一个实现IDataConnection的SQLConnector类来演示这一点。

创建类时,Visual Studio会警告合同未履行。 Tim演示如何使用"Implement Interface"自动生成CreatePrize方法。他还解释了NotImplementedException框架及其存在的原因——它允许代码编译,而不假装方法有效。

创建SQL和文本连接器

Tim添加了SQLConnector类和TextConnector类,均实现IDataConnection。 他解释尽管保存到SQL数据库和保存到文本文件是非常不同的过程,但它们都满足相同的接口合同。

他现在添加了一个简单的示例返回值,并放置了TODO注释以提醒自己在稍后实现真正的保存逻辑。 这使应用程序在继续课程的同时仍能正常运行。

最终设置:连接全局配置

Tim回到GlobalConfig类并连接实际的连接。 他展示了如何初始化Connections列表并添加SQLConnector和TextConnector的实例。

他解释为什么需要两个独立的if语句而不是if-else——因为用户可能希望同时保存到两个数据源。

在哪里调用InitializeConnections?

Tim解释说,InitializeConnections必须在应用程序启动时调用。 他修改Program.cs并调用:

GlobalConfig.InitializeConnections(true, true);
Text

在启动表单之前。 这确保了连接列表准备就绪,并可在整个应用程序中访问。

然后,他将启动表单更改为CreatePrizeForm,以便可以立即测试功能。

验证奖项表单

Tim打开表单并解释第一个任务:验证四个字段。 他更喜欢保持事件处理程序简洁,因此创建了一个名为ValidateForm()的私有方法。

Tim解释此方法可以从任何地方调用,不仅仅是按钮点击。 它返回一个布尔值,指示表单是否有效。 他展示了他使用输出变量的模式:

bool output = true; return output;
Text

他说他喜欢以true开始,因为在出现问题时将其更改为false比在每次检查后设置为true更容易。

检查名次编号

Tim解释第一个验证:名次编号必须大于零的整数。

他使用int.TryParse将PlaceNumberValue.Text(字符串)转换为整数。 Tim分解了TryParse的工作原理:

  • 它接受一个字符串,并尝试将其转换为数字。

  • 它返回一个指示成功或失败的布尔值。

  • 它使用输出参数输出转换后的值。

Tim强调TryParse比Parse更安全,因为它不会因错误的输入而崩溃——而是返回false并将输出设置为零。

然后他解释了逻辑:

  • 如果placeNumberValidNumber为false,则设置output = false。

  • 如果placeNumber < 1,则设置output = false。

Tim警告在此处使用else语句是不合适的,因为此方法具有多个检查。 如果一个检查失败,方法仍应评估其他检查以收集所有错误。

验证名次名称

Tim转向下一个验证:名次名称不能为空。

他检查:

if (placeNameValue.Text.Length == 0) { output = false; }
Text

Tim解释在真实应用中,您将为每个失败的验证显示错误消息。 但现在,他保持简单,只返回true/false。

验证奖金额与奖项比例

Tim解释表单必须包含奖金额或奖项比例(其中之一必须大于零)。 他指出重要的不同:

  • 奖项比例是一个整数(int)

  • 奖金额是一个小数(decimal),因为金钱可以包含分。

他创建了变量:

decimal prizeAmount = 0; int prizePercentage = 0;
Text

然后他对两者使用TryParse:

bool prizeAmountValid = decimal.TryParse(prizeAmountValue.Text, out prizeAmount); bool prizePercentageValid = int.TryParse(prizePercentageValue.Text, out prizePercentage);
Text

Tim解释两者都必须是有效数字。 如果任一无效,表单则无效。

接下来,他检查至少一个大于零:

if (prizeAmount <= 0 && prizePercentage <= 0) { output = false; }
Text

Tim还添加了一个检查以确保百分比在0和100之间:

if (prizePercentage < 0||prizePercentage > 100) { output = false; }
Text

他解释原因:150%意味着您将发放超过奖品池的东西,这是不可能的。

使用验证结果

所有检查完成后,Tim解释如何使用结果:

if (ValidateForm()) { // create model and save } else { MessageBox.Show("此表单包含无效信息。 请检查并重试。"); }
Text

Tim指出您可以在第一次失败时提早返回,但他选择运行所有检查,以便用户可以一次看到所有验证错误。 这样可以减少挫折,因为他们可以一次性修复一切问题。

创建PrizeModel

Tim解释表单一旦有效,下一步是创建PrizeModel。

他演示如何实例化一个模型:

PrizeModel model = new PrizeModel(); model.PlaceName = placeNameValue.Text; model.PlaceNumber = placeNumberValue.Text; // 问题:这是一个字符串
Text

Tim强调问题:PlaceNumber是一个int,但表单值是一个字符串。 为了解决这个问题,他解释了两种选择:

  • 在表单中再次解析每个值(重复)。

  • 在PrizeModel中添加一个构造函数重载。

Tim选择了选项2。

在PrizeModel中重载构造函数

Tim添加了一个重载的构造函数,它接受四个字符串:

public PrizeModel(string placeName, string placeNumber, string prizeAmount, string prizePercentage)
{
PlaceName = placeName;
    PlaceNumber = int.TryParse(placeNumber, out int placeNumberValue) ? placeNumberValue : 0;
    PrizeAmount = decimal.TryParse(prizeAmount, out decimal prizeAmountValue) ? prizeAmountValue : 0;
    PrizePercentage = double.TryParse(prizePercentage, out double prizePercentageValue) ? prizePercentageValue : 0;
}
Text

Tim解释不在乎解析是否失败,因为它将默认为零,这本来就是数字的默认值。

这个构造函数允许表单直接使用字符串输入创建PrizeModel,并让模型处理解析。

使用IDataConnection保存模型

模型存在后,Tim解释如何使用全局连接列表保存它。

他使用foreach循环:

foreach (IDataConnection db in GlobalConfig.Connections) { db.CreatePrize(model); }

Tim解释这段循环调用每个连接(SQL和文本文件)上的CreatePrize()。 即使方法尚未实现,表单也能正常工作并假装保存数据。 这证明了接口和全局配置模式是有效的。

测试表单

Tim强调尽早测试的重要性。 他添加断点并运行应用程序。

  • 他首先测试一个空表单。

  • 他逐步执行ValidateForm()。

  • 他发现输出为false,验证失败,如预期一样。

  • 然后他填入有效的数据,确认构造函数正确初始化模型。

  • 他还确认循环遍历了两个连接。

Tim演示了表单是功能正常的,并且模式验证成功。

最终清理:清空表单

Tim做了一些最后的调整:

  • 成功创建奖品后,清空表单字段。

  • 为奖金额和百分比设置默认值为0,以便用户每次不必输入零。

然后他确认表单在有效提交后正确清空。

接下来是什么?

Tim以视频结束,并表示下一步是连接SQL和文本连接类,以便实际保存数据。

他提醒观众关注下一课,在那里他将实现SQL连接器并实际连接到数据库。

Earn More by Sharing What You Love

Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Let's Stay in Touch!

Join our newsletter, you’ll get exclusive access on article updates. We value your privacy

Key in blue circle

立即获取免费的 30 天试用版密钥

Your trial license will be sent to your email address

无任何限制。100% 解锁。无需信用卡。

bullet_checked无需信用卡或创建账户无任何限制。100% 解锁。无需信用卡。
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
获取您的无义务咨询
填写下面的表格或通过sales@ironsoftware.com
您的资料将始终保密。
深受全球数百万工程师信赖
Iron Software 的客户徽标
立即获取您的免费30 天试用密钥
无需信用卡或创建账户