Skip to content

feat(Result): 扩展Result结构体功能并优化实现#93

Merged
GeWuYou merged 2 commits into
mainfrom
feat/result-add-try-map-bind-methods
Mar 10, 2026
Merged

feat(Result): 扩展Result结构体功能并优化实现#93
GeWuYou merged 2 commits into
mainfrom
feat/result-add-try-map-bind-methods

Conversation

@GeWuYou
Copy link
Copy Markdown
Owner

@GeWuYou GeWuYou commented Mar 10, 2026

  • 添加StructLayout特性优化内存布局
  • 将Failure方法中的Exception替换为InvalidOperationException
  • 重构Equals方法提高比较逻辑的可读性
  • 简化GetHashCode方法的哈希计算逻辑
  • 更新ToString方法移除空值检查
  • 新增Try方法用于安全执行可能抛出异常的操作
  • 添加Map方法支持将Result成功状态映射到其他类型
  • 实现Bind方法用于链式调用Result操作

Summary by Sourcery

通过为 Result 类型增加更多函数式辅助方法,并优化其在失败处理、相等性比较和表示方面的行为。

新特性:

  • 添加 Try 辅助方法,用于安全地执行操作,并将抛出的异常转换为失败的 Result。
  • 添加 Map 方法,将成功的 Result 转换为另一种类型的 Result,同时保留失败状态。
  • 添加 Bind 方法,以支持对返回 Result 值的操作进行链式调用。

改进:

  • 为 Result 添加 StructLayout 注解,以优化其内存布局。
  • 将 Failure(string) 修改为创建 InvalidOperationException 实例,而不是通用的 Exception 实例。
  • 简化并明确 Result 的相等性比较、哈希计算和字符串表示逻辑。
Original summary in English

Summary by Sourcery

Extend the Result type with additional functional helpers and refine its behavior for failures, equality, and representation.

New Features:

  • Add a Try helper to execute actions safely and convert thrown exceptions into failure Results.
  • Add a Map method to transform successful Results into Results of another type while preserving failures.
  • Add a Bind method to support chaining operations that return Result values.

Enhancements:

  • Annotate Result with StructLayout to optimize its memory layout.
  • Change Failure(string) to create InvalidOperationException instances instead of generic Exception instances.
  • Simplify and clarify equality, hashing, and string representation logic for Result.

- 添加StructLayout特性优化内存布局
- 将Failure方法中的Exception替换为InvalidOperationException
- 重构Equals方法提高比较逻辑的可读性
- 简化GetHashCode方法的哈希计算逻辑
- 更新ToString方法移除空值检查
- 新增Try方法用于安全执行可能抛出异常的操作
- 添加Map方法支持将Result成功状态映射到其他类型
- 实现Bind方法用于链式调用Result操作
@deepsource-io
Copy link
Copy Markdown

deepsource-io Bot commented Mar 10, 2026

DeepSource Code Review

We reviewed changes in 8e4e794...c6024bf on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
C# Mar 10, 2026 1:10p.m. Review ↗
Secrets Mar 10, 2026 1:10p.m. Review ↗

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Mar 10, 2026

审阅者指南

在保持内部不变式和内存布局更严格的前提下,为函数式的 Result 结构体扩展了单子(monadic)工具和改进后的 API,包括新的 Try / Map / Bind 辅助方法,以及更简化的相等性比较、哈希和格式化逻辑。

Result.Try、Map 和 Bind 链式调用的时序图

sequenceDiagram
    actor Client
    participant Result
    participant ActionDelegate
    participant Result_T as Result~T~

    Client->>Result: Try(action)
    activate Result
    Result->>ActionDelegate: Invoke()
    activate ActionDelegate
    ActionDelegate-->>Result: completes or throws
    deactivate ActionDelegate

    alt action succeeds
        Result-->>Client: Result.Success()
    else action throws ex
        Result-->>Client: Result.Failure(ex)
    end
    deactivate Result

    Client->>Result_T: Map(mapFunc) on success Result
    activate Result_T
    alt source IsSuccess
        Result_T->>Result_T: mapFunc()
        Result_T-->>Client: Success(mappedValue)
    else source IsFailure
        Result_T-->>Client: Failure(originalException)
    end
    deactivate Result_T

    Client->>Result_T: Bind(bindFunc) on mapped Result
    activate Result_T
    alt source IsSuccess
        Result_T->>Result_T: bindFunc()
        Result_T-->>Client: Result~U~ from bindFunc
    else source IsFailure
        Result_T-->>Client: Failure(originalException)
    end
    deactivate Result_T
Loading

带单子工具的扩展 Result 结构体类图

classDiagram
    class Result {
        - bool _isSuccess
        - Exception _exception
        + bool IsSuccess
        + bool IsFailure
        + static Result Success()
        + static Result Failure(Exception ex)
        + static Result Failure(string message)
        + Result~A~ ToResult~A~(A value)
        + bool Equals(Result other)
        + int GetHashCode()
        + string ToString()
        + static Result Try(Action action)
        + Result~B~ Map~B~(Func~B~ func)
        + Result~B~ Bind~B~(Func~Result~B~~ func)
    }

    class Result~T~ {
        - bool _isSuccess
        - T _value
        - Exception _exception
        + bool IsSuccess
        + bool IsFailure
        + T Value
        + static Result~T~ Success(T value)
        + static Result~T~ Failure(Exception ex)
        + bool Equals(Result~T~ other)
        + int GetHashCode()
        + string ToString()
    }

    Result <.. Result~T~ : used_by
Loading

文件级变更

变更 详情 文件
收紧 Result 不变式并调整构造方式和内存布局
  • Result 结构体添加 StructLayout(LayoutKind.Auto) 特性,以影响其内存布局
  • Failure(string) 工厂方法改为构造 InvalidOperationException 而不是通用的 Exception,同时保持原有的消息负载不变
  • 在失败场景中依赖非空的 _exception 字段,并在合适的位置使用空值忽略运算符(null-forgiving operator)
GFramework.Core/Functional/Result.cs
重构 Result 的相等性、哈希和字符串格式化逻辑
  • 重写 Equals(Result):先比较成功标志;若双方都为成功则直接返回 true,否则在失败情况下假定异常非空,比较异常类型和消息
  • 简化 GetHashCode():在成功时返回常量 1;在失败时返回异常类型与消息的组合哈希值,并使用非空断言
  • 简化 ToString():在成功时返回 "Success",在失败时返回 "Fail(message)",不再为消息提供空值兜底
GFramework.Core/Functional/Result.cs
为执行、映射和绑定 Result 添加函数式辅助方法
  • 引入静态 Try(Action) 方法,将一个 Action 包装为 Result:无异常时返回 Success,有异常时返回 Failure(ex)
  • 添加实例方法 Map<B>(Func<B>):在成功时调用 func() 并生成 Result<B>.Success(func());在失败时通过 Result<B>.Failure(_exception!) 传播存储的异常
  • 添加实例方法 Bind<B>(Func<Result<B>>):在成功时直接返回 func() 的结果;在失败时通过 Result<B>.Failure(_exception!) 传播存储的异常
GFramework.Core/Functional/Result.cs

提示与命令

与 Sourcery 交互

  • 触发新的审阅: 在拉取请求(pull request)中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审阅评论。
  • 从审阅评论生成 GitHub Issue: 在某条审阅评论下回复,请求 Sourcery 从该评论创建一个 Issue。你也可以直接回复 @sourcery-ai issue,从该评论创建 Issue。
  • 生成拉取请求标题: 在拉取请求标题的任意位置写上 @sourcery-ai,即可在任意时间生成标题。你也可以在拉取请求中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成拉取请求摘要: 在拉取请求正文的任意位置写上 @sourcery-ai summary,即可在任意位置生成 PR 摘要。你也可以在拉取请求中评论 @sourcery-ai summary 来(重新)生成摘要。
  • 生成审阅者指南: 在拉取请求中评论 @sourcery-ai guide,即可在任意时间(重新)生成审阅者指南。
  • 解决所有 Sourcery 评论: 在拉取请求中评论 @sourcery-ai resolve,以解决所有 Sourcery 评论。若你已经处理完所有评论且不希望再看到它们,这将非常有用。
  • 忽略所有 Sourcery 审阅: 在拉取请求中评论 @sourcery-ai dismiss,以忽略所有现有的 Sourcery 审阅。若你希望从头开始一次新的审阅,尤其有用——别忘了再评论一次 @sourcery-ai review 来触发新的审阅!

自定义你的使用体验

访问你的 控制面板 以:

  • 启用或禁用审阅功能,例如 Sourcery 生成的拉取请求摘要、审阅者指南等。
  • 更改审阅语言。
  • 添加、移除或编辑自定义审阅说明。
  • 调整其他审阅设置。

获取帮助

Original review guide in English

Reviewer's Guide

Extends the functional Result struct with monadic utilities and improved APIs while tightening its internal invariants and memory layout, including new Try/Map/Bind helpers and simplified equality, hashing, and formatting logic.

Sequence diagram for Result.Try, Map, and Bind chaining

sequenceDiagram
    actor Client
    participant Result
    participant ActionDelegate
    participant Result_T as Result~T~

    Client->>Result: Try(action)
    activate Result
    Result->>ActionDelegate: Invoke()
    activate ActionDelegate
    ActionDelegate-->>Result: completes or throws
    deactivate ActionDelegate

    alt action succeeds
        Result-->>Client: Result.Success()
    else action throws ex
        Result-->>Client: Result.Failure(ex)
    end
    deactivate Result

    Client->>Result_T: Map(mapFunc) on success Result
    activate Result_T
    alt source IsSuccess
        Result_T->>Result_T: mapFunc()
        Result_T-->>Client: Success(mappedValue)
    else source IsFailure
        Result_T-->>Client: Failure(originalException)
    end
    deactivate Result_T

    Client->>Result_T: Bind(bindFunc) on mapped Result
    activate Result_T
    alt source IsSuccess
        Result_T->>Result_T: bindFunc()
        Result_T-->>Client: Result~U~ from bindFunc
    else source IsFailure
        Result_T-->>Client: Failure(originalException)
    end
    deactivate Result_T
Loading

Class diagram for extended Result struct with monadic utilities

classDiagram
    class Result {
        - bool _isSuccess
        - Exception _exception
        + bool IsSuccess
        + bool IsFailure
        + static Result Success()
        + static Result Failure(Exception ex)
        + static Result Failure(string message)
        + Result~A~ ToResult~A~(A value)
        + bool Equals(Result other)
        + int GetHashCode()
        + string ToString()
        + static Result Try(Action action)
        + Result~B~ Map~B~(Func~B~ func)
        + Result~B~ Bind~B~(Func~Result~B~~ func)
    }

    class Result~T~ {
        - bool _isSuccess
        - T _value
        - Exception _exception
        + bool IsSuccess
        + bool IsFailure
        + T Value
        + static Result~T~ Success(T value)
        + static Result~T~ Failure(Exception ex)
        + bool Equals(Result~T~ other)
        + int GetHashCode()
        + string ToString()
    }

    Result <.. Result~T~ : used_by
Loading

File-Level Changes

Change Details Files
Tighten Result invariants and adjust construction and layout
  • Add StructLayout(LayoutKind.Auto) attribute to the Result struct to influence memory layout
  • Change the Failure(string) factory to construct InvalidOperationException instead of a generic Exception, keeping the same message payload
  • Rely on non-null _exception for failure cases by using null-forgiving operator where appropriate
GFramework.Core/Functional/Result.cs
Refactor equality, hashing, and string formatting for Result
  • Rewrite Equals(Result) to first compare success flags, return true for both-success, and otherwise compare exception type and message assuming non-null exceptions in failure cases
  • Simplify GetHashCode() to return constant 1 for success and a combined hash of exception type and message for failure, using non-null assertions
  • Simplify ToString() to return "Success" for successful results and "Fail(message)" for failures without a null fallback for the message
GFramework.Core/Functional/Result.cs
Add functional helpers for executing, mapping, and binding Result
  • Introduce static Try(Action) to wrap an action in a Result, returning Success on no exception and Failure(ex) on exception
  • Add Map(Func) instance method that, for success, produces Result.Success(func()) and for failure propagates the stored exception via Result.Failure(_exception!)
  • Add Bind(Func<Result>) instance method that, for success, returns func() directly and for failure propagates the stored exception via Result.Failure(_exception!)
GFramework.Core/Functional/Result.cs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些高层次的反馈:

  • 多个成员在失败时假设 _exception 为非空(EqualsGetHashCodeToStringMapBind),这会在 default(Result) 或任何未携带异常就创建的失败结果上导致异常;建议要么在实现中防御性地处理默认/无效状态,要么在构造时更严格地强制 _exception 非空的约束。
  • TryMapBind 接受委托参数,但没有对其进行校验;添加显式的空检查(例如 ArgumentNullException.ThrowIfNull)可以让误用尽早失败,并避免在调用期间出现让人困惑的 NullReferenceException
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Several members assume `_exception` is non-null on failure (`Equals`, `GetHashCode`, `ToString`, `Map`, `Bind`), which can throw for `default(Result)` or any failure created without an exception; consider defensively handling the default/invalid state or enforcing non-null construction more strictly.
- `Try`, `Map`, and `Bind` accept delegate parameters but do not validate them; adding explicit null checks (e.g., `ArgumentNullException.ThrowIfNull`) would make misuse fail fast and avoid confusing `NullReferenceException`s during invocation.

## Individual Comments

### Comment 1
<location path="GFramework.Core/Functional/Result.cs" line_range="124-125" />
<code_context>
+        if (_isSuccess)
+            return true;
+
+        return _exception!.GetType() == other._exception!.GetType() &&
+               _exception.Message == other._exception.Message;
     }

</code_context>
<issue_to_address>
**issue (bug_risk):** Relying on `_exception!` being non-null introduces a hidden invariant and potential NullReferenceExceptions.

This assumes that whenever `_isSuccess` is `false`, both `_exception` and `other._exception` are non-null, and relies on the null-forgiving operator here and in `GetHashCode`, `ToString`, `Map`, and `Bind`. If a failure `Result` is ever created with a null exception (e.g., via the private ctor or future changes), these members will throw `NullReferenceException` at use sites. Either (a) enforce the invariant at construction time (e.g., throw when `isSuccess == false && ex is null`), or (b) keep the comparison and hash code logic null-safe as before so the type is more robust to future changes and misuse.
</issue_to_address>

Sourcery 对开源项目免费 —— 如果你觉得我们的代码评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据这些反馈改进后续的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • Several members assume _exception is non-null on failure (Equals, GetHashCode, ToString, Map, Bind), which can throw for default(Result) or any failure created without an exception; consider defensively handling the default/invalid state or enforcing non-null construction more strictly.
  • Try, Map, and Bind accept delegate parameters but do not validate them; adding explicit null checks (e.g., ArgumentNullException.ThrowIfNull) would make misuse fail fast and avoid confusing NullReferenceExceptions during invocation.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Several members assume `_exception` is non-null on failure (`Equals`, `GetHashCode`, `ToString`, `Map`, `Bind`), which can throw for `default(Result)` or any failure created without an exception; consider defensively handling the default/invalid state or enforcing non-null construction more strictly.
- `Try`, `Map`, and `Bind` accept delegate parameters but do not validate them; adding explicit null checks (e.g., `ArgumentNullException.ThrowIfNull`) would make misuse fail fast and avoid confusing `NullReferenceException`s during invocation.

## Individual Comments

### Comment 1
<location path="GFramework.Core/Functional/Result.cs" line_range="124-125" />
<code_context>
+        if (_isSuccess)
+            return true;
+
+        return _exception!.GetType() == other._exception!.GetType() &&
+               _exception.Message == other._exception.Message;
     }

</code_context>
<issue_to_address>
**issue (bug_risk):** Relying on `_exception!` being non-null introduces a hidden invariant and potential NullReferenceExceptions.

This assumes that whenever `_isSuccess` is `false`, both `_exception` and `other._exception` are non-null, and relies on the null-forgiving operator here and in `GetHashCode`, `ToString`, `Map`, and `Bind`. If a failure `Result` is ever created with a null exception (e.g., via the private ctor or future changes), these members will throw `NullReferenceException` at use sites. Either (a) enforce the invariant at construction time (e.g., throw when `isSuccess == false && ex is null`), or (b) keep the comparison and hash code logic null-safe as before so the type is more robust to future changes and misuse.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread GFramework.Core/Functional/Result.cs
- 在构造函数中添加强制不变式检查,确保失败状态必须携带非空异常
- 为Try方法添加空值验证,防止传入空委托导致异常
- 为Map方法添加空值验证,增强方法调用的安全性
- 为Bind方法添加空值验证,提升代码健壮性
- 重构Map和Bind方法结构,使其更清晰易读
@GeWuYou GeWuYou merged commit 11c7bc1 into main Mar 10, 2026
8 checks passed
@GeWuYou GeWuYou deleted the feat/result-add-try-map-bind-methods branch March 10, 2026 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant