Add try-catch-finally code generation support#10
Conversation
Agent-Logs-Url: https://github.com/fengb3/EasyCodeBuilder/sessions/6e5dcf18-3a2b-4da1-a078-791854f18069 Co-authored-by: fengb3 <45181245+fengb3@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds first-class C# code generation support for try/catch/finally blocks, integrating it into the existing option-based/fluent builder API used throughout Fengb3.EasyCodeBuilder.Csharp.
Changes:
- Introduces
TryOption,CatchOption,FinallyOption, plus fluent extension methods for configuring catch signatures and filters. - Extends
MethodOptionwith.Try(...)to add atrystatement into a generated method body. - Adds xUnit tests covering common
try/catch/finallygeneration scenarios.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| EasyCodeBuilder/Csharp/TryCatchFinallyOption.cs | Implements the new try/catch/finally option types and fluent configuration extensions. |
| EasyCodeBuilder/Csharp/MethodOption.cs | Adds MethodOption.Try(...) to integrate try/catch/finally into method generation. |
| EasyCodeBuilder.Test/Csharp/TryCatchFinallyTests.cs | Adds test coverage for generated try/catch/finally output. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| BeforeChildren?.Invoke(cb); | ||
| cb.CodeBlock(OnChildren, "try"); | ||
| _clauses?.Invoke(cb); |
There was a problem hiding this comment.
TryOption.Build can emit a try { ... } block with no catch or finally when no clauses are added (_clauses is null). In C#, a try statement must have at least one catch or a finally, so this output can never be valid. Consider validating in Build (e.g., throw an InvalidOperationException with a clear message) or otherwise ensuring at least one clause is present before rendering.
| BeforeChildren?.Invoke(cb); | |
| cb.CodeBlock(OnChildren, "try"); | |
| _clauses?.Invoke(cb); | |
| if (_clauses is null) | |
| { | |
| throw new InvalidOperationException("A try statement must include at least one catch or finally clause."); | |
| } | |
| BeforeChildren?.Invoke(cb); | |
| cb.CodeBlock(OnChildren, "try"); | |
| _clauses.Invoke(cb); |
| private CodeRenderFragment? _clauses; | ||
|
|
||
| /// <summary> | ||
| /// 构建代码 | ||
| /// </summary> | ||
| /// <param name="cb">代码构建器</param> | ||
| /// <returns>代码构建器</returns> | ||
| public override CodeBuilder Build(CodeBuilder cb) | ||
| { | ||
| BeforeChildren?.Invoke(cb); | ||
| cb.CodeBlock(OnChildren, "try"); | ||
| _clauses?.Invoke(cb); | ||
| return cb; | ||
| } | ||
|
|
||
| internal void AddClause(CodeRenderFragment clause) | ||
| { | ||
| _clauses += clause; | ||
| } |
There was a problem hiding this comment.
Clauses are stored via a multicast delegate (_clauses += clause), which makes it easy to generate invalid C# such as multiple finally blocks or catch clauses appearing after a finally (depending on call order). If this API is meant to generate valid C# by construction, consider storing catches in a list plus an optional single FinallyOption, and always render finally last (or throw if Catch() is called after Finally()).
| public static TryOption Catch(this TryOption tryOption, Action<CatchOption> configure) | ||
| { | ||
| var catchOption = new CatchOption(); | ||
| configure(catchOption); | ||
| tryOption.AddClause(catchOption.Build); | ||
| return tryOption; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// add a finally clause to the try block | ||
| /// </summary> | ||
| /// <param name="tryOption">try 语句选项</param> | ||
| /// <param name="configure">finally 语句配置委托</param> | ||
| /// <returns>try 语句选项</returns> | ||
| public static TryOption Finally(this TryOption tryOption, Action<FinallyOption> configure) | ||
| { | ||
| var finallyOption = new FinallyOption(); | ||
| configure(finallyOption); | ||
| tryOption.AddClause(finallyOption.Build); | ||
| return tryOption; |
There was a problem hiding this comment.
Catch(...) and Finally(...) invoke configure(...) directly. For consistency with the existing AddChild helpers (which tolerate null delegates), consider using a null-conditional invoke or explicitly throwing ArgumentNullException so the behavior is intentional and consistent across the fluent API surface.
| [Fact] | ||
| public void TryOnly() | ||
| { | ||
| var code = new MethodOption() | ||
| .WithKeyword("public") | ||
| .WithName("Run") | ||
| .WithReturnType("void") | ||
| .Try(@try => | ||
| { | ||
| @try.AppendLine("DoWork();"); | ||
| }) | ||
| .Build(); | ||
|
|
||
| var expected = """ | ||
| public void Run() | ||
| { | ||
| try | ||
| { | ||
| DoWork(); | ||
| } | ||
| } | ||
| """; | ||
|
|
||
| Assert.Equal(Norm(expected), Norm(code)); | ||
| output.WriteLine(code); | ||
| } |
There was a problem hiding this comment.
This test asserts that generating a try without any catch/finally is supported. Since try { } without at least one clause is not valid C#, this test will need to be updated/removed if TryOption is changed to enforce valid syntax (recommended).
|
@copilot apply changes based on the comments in this thread |
…ecks Agent-Logs-Url: https://github.com/fengb3/EasyCodeBuilder/sessions/fb7d96bc-d23c-4621-b3b9-a8f7ebb66416 Co-authored-by: fengb3 <45181245+fengb3@users.noreply.github.com>
Applied all four review changes in e3f2c34:
|
The library had no support for generating
try/catch/finallyblocks. This adds first-class options for all three clauses, consistent with the existing control-flow option pattern (IfOption,LoopOption, etc.).New types (
TryCatchFinallyOption.cs)TryOption— renders thetry { }block; owns its catch/finally clauses internally so the entire construct is encapsulated in a single child added to the parentCatchOption— supports barecatch,catch (Type),catch (Type var), andcatch (Type var) when (filter)FinallyOption— renders thefinally { }blockTryCatchFinallyOptionExtensions— fluent.Catch(),.Finally(),.WithExceptionType(),.WithVariableName(),.WithWhenFilter()Extension on
MethodOptionAdded
MethodOption.Try(configure)to integrate naturally with the existing fluent method-building API.Usage
Generates: