- 虚拟时钟:实训台时钟面板可拨时间、1×/10×/60× 倍速、恢复真实时间;编译时把 DateTime.Now / Today / UtcNow 改写成 SimClock;评分规则加 clock 步骤 - 项目资源(Properties.Resources):平台素材库(自绘开关图片、合成门铃声),"项目资源"对话框勾选, 平台生成 Properties/Resources.Designer.cs;设计器图片框的"图片"属性可选资源;程序目录下的 Resources 指向素材库;未添加资源时给中文提示 - Windows Media Player(AxWMPLib):设置 URL 自动播放、Ctlcontrols.play/stop,浏览器里播放声音, 实训台加广播喇叭;编译时给窗体嵌空资源,OcxState 取到 null 不报错;图片和声音统一按内容缓存(blob) - TrackBar:拖动、点轨道、方向键,先 ValueChanged 再 Scroll;设计器加"滑动条""媒体播放器" - 评分规则加 value、media 检查;示例项目 7 任务 1–3(修正书上开关灯判断写反、漏填串口列表) - 修正:Properties/ 下的资源类不当作窗体;带命名空间的 System.AppDomain.CurrentDomain.BaseDirectory 放行 - 测试:虚拟时钟、TrackBar、项目 7 示例评分、作业起点带资源类 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
70 lines
2.9 KiB
C#
70 lines
2.9 KiB
C#
using System.Text;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
using Microsoft.CodeAnalysis.Text;
|
|
|
|
namespace Sim.Runner;
|
|
|
|
/// <summary>
|
|
/// 编译前把学生代码里的 DateTime.Now / Today / UtcNow 改写成虚拟时钟 Sim.Host.SimClock(实训台上可以拨时间、调倍速,
|
|
/// 教材项目 7 按 6:45 起床、22:00 熄灯等时刻切换情景模式)。只改写语义上确实是 System.DateTime 这三个属性的地方;
|
|
/// 改写不换行,报错和异常里的行号不变。学生在编辑器里看到的仍是原来的代码。
|
|
/// </summary>
|
|
internal static class ClockRewriter
|
|
{
|
|
private const string Target = "global::Sim.Host.SimClock";
|
|
|
|
public static CSharpCompilation Rewrite(CSharpCompilation compilation)
|
|
{
|
|
var result = compilation;
|
|
foreach (var tree in compilation.SyntaxTrees)
|
|
{
|
|
var text = tree.GetText().ToString();
|
|
if (!text.Contains("Now") && !text.Contains("Today")) continue;
|
|
var rewriter = new Rewriter(compilation.GetSemanticModel(tree));
|
|
var root = rewriter.Visit(tree.GetRoot());
|
|
if (!rewriter.Changed) continue;
|
|
result = result.ReplaceSyntaxTree(tree,
|
|
CSharpSyntaxTree.ParseText(SourceText.From(root.ToFullString(), Encoding.UTF8), (CSharpParseOptions)tree.Options, tree.FilePath));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static string? Replacement(ISymbol? symbol) =>
|
|
symbol is IPropertySymbol { IsStatic: true, Name: "Now" or "Today" or "UtcNow" } property
|
|
&& property.ContainingType.SpecialType == SpecialType.System_DateTime
|
|
? $"{Target}.{property.Name}"
|
|
: null;
|
|
|
|
private sealed class Rewriter(SemanticModel model) : CSharpSyntaxRewriter
|
|
{
|
|
public bool Changed { get; private set; }
|
|
|
|
public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
|
|
{
|
|
// DateTime.Now、System.DateTime.Now
|
|
if (Replacement(model.GetSymbolInfo(node).Symbol) is { } replacement)
|
|
{
|
|
Changed = true;
|
|
return SyntaxFactory.ParseExpression(replacement).WithTriviaFrom(node);
|
|
}
|
|
return base.VisitMemberAccessExpression(node);
|
|
}
|
|
|
|
public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node)
|
|
{
|
|
// using static System.DateTime; 之后直接写 Now
|
|
if (node.Parent is not MemberAccessExpressionSyntax access || access.Expression == node)
|
|
{
|
|
if (Replacement(model.GetSymbolInfo(node).Symbol) is { } replacement)
|
|
{
|
|
Changed = true;
|
|
return SyntaxFactory.ParseExpression(replacement).WithTriviaFrom(node);
|
|
}
|
|
}
|
|
return base.VisitIdentifierName(node);
|
|
}
|
|
}
|
|
}
|