mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
feat(functional): 添加函数式编程扩展和Option类型支持
- 在FunctionExtensions中新增Map扩展方法用于对象映射 - 在PipeExtensions中新增On扩展方法用于值到函数的应用 - 移除Tap方法及相关测试以优化管道操作 - 新增NullableExtensions实现可空类型到Option的转换 - 新增Option结构体提供安全的可选值处理 - 新增OptionExtensions提供Map、Bind、Filter、Match等函数式操作 - 新增OptionValueExtensions提供GetOrElse和OrElse值提取方法 - 调整全局引用添加Concurrent集合支持 - 扩展IResetApplyAbleSettings接口添加Data属性 - 更新Godot设置类实现Data属性返回设置数据模型
This commit is contained in:
parent
c93d32c495
commit
fd3a9ae9e0
@ -10,8 +10,6 @@ namespace GFramework.Core.Tests.functional.pipe;
|
||||
[TestFixture]
|
||||
public class PipeExtensionsTests
|
||||
{
|
||||
#region Pipe Tests
|
||||
|
||||
/// <summary>
|
||||
/// 测试Pipe方法 - 验证值能够正确传递给函数并返回结果
|
||||
/// </summary>
|
||||
@ -28,10 +26,6 @@ public class PipeExtensionsTests
|
||||
Assert.That(result, Is.EqualTo(10));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Then Tests
|
||||
|
||||
/// <summary>
|
||||
/// 测试Then方法 - 验证两个函数能够正确组合执行
|
||||
/// </summary>
|
||||
@ -52,10 +46,6 @@ public class PipeExtensionsTests
|
||||
int MultiplyByThree(int x) => x * 3;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region After Tests
|
||||
|
||||
/// <summary>
|
||||
/// 测试After方法 - 验证反向函数组合的正确性
|
||||
/// </summary>
|
||||
@ -76,32 +66,6 @@ public class PipeExtensionsTests
|
||||
int AddTwo(int x) => x + 2;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tap Tests
|
||||
|
||||
/// <summary>
|
||||
/// 测试Tap方法 - 验证副作用操作执行后返回原值
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Tap_Should_Execute_Action_And_Return_Original_Value()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
var capturedValue = 0;
|
||||
|
||||
// Act
|
||||
var result = value.Tap(x => capturedValue = x);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(42));
|
||||
Assert.That(capturedValue, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Apply Tests
|
||||
|
||||
/// <summary>
|
||||
/// 测试Apply方法 - 验证函数能够正确应用到参数上
|
||||
/// </summary>
|
||||
@ -118,10 +82,6 @@ public class PipeExtensionsTests
|
||||
Assert.That(result, Is.EqualTo(10));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Also Tests
|
||||
|
||||
/// <summary>
|
||||
/// 测试Also方法 - 验证执行操作后返回原值功能
|
||||
/// </summary>
|
||||
@ -140,10 +100,6 @@ public class PipeExtensionsTests
|
||||
Assert.That(capturedValue, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Let Tests
|
||||
|
||||
/// <summary>
|
||||
/// 测试Let方法 - 验证值转换功能
|
||||
/// </summary>
|
||||
@ -159,6 +115,4 @@ public class PipeExtensionsTests
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(10));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -10,6 +10,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
namespace GFramework.Core.functional.functions;
|
||||
|
||||
/// <summary>
|
||||
@ -74,6 +75,7 @@ public static class FunctionExtensions
|
||||
{
|
||||
result = func(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -122,4 +124,17 @@ public static class FunctionExtensions
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map:对单个对象应用函数
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">源对象类型</typeparam>
|
||||
/// <typeparam name="TResult">映射后的类型</typeparam>
|
||||
/// <param name="source">要映射的源对象</param>
|
||||
/// <param name="selector">转换函数</param>
|
||||
/// <returns>映射后的对象</returns>
|
||||
public static TResult Map<TSource, TResult>(
|
||||
this TSource source,
|
||||
Func<TSource, TResult> selector)
|
||||
=> selector(source);
|
||||
}
|
||||
@ -10,6 +10,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
namespace GFramework.Core.functional.pipe;
|
||||
|
||||
/// <summary>
|
||||
@ -58,21 +59,6 @@ public static class PipeExtensions
|
||||
Func<TSource, TMiddle> first)
|
||||
=> x => second(first(x));
|
||||
|
||||
/// <summary>
|
||||
/// Tap:执行副作用操作但返回原值(用于调试、日志等)
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">输入值的类型</typeparam>
|
||||
/// <param name="value">要执行操作的输入值</param>
|
||||
/// <param name="action">要执行的副作用操作</param>
|
||||
/// <returns>原始输入值</returns>
|
||||
public static TSource Tap<TSource>(
|
||||
this TSource value,
|
||||
Action<TSource> action)
|
||||
{
|
||||
action(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply:将函数应用于值(柯里化辅助)
|
||||
/// </summary>
|
||||
@ -86,6 +72,19 @@ public static class PipeExtensions
|
||||
TSource value)
|
||||
=> func(value);
|
||||
|
||||
/// <summary>
|
||||
/// On:将值应用于函数(与Apply功能相同,但参数顺序相反)
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">输入值的类型</typeparam>
|
||||
/// <typeparam name="TResult">函数返回结果的类型</typeparam>
|
||||
/// <param name="value">要传递给函数的输入值</param>
|
||||
/// <param name="func">要应用的函数</param>
|
||||
/// <returns>函数执行后的结果</returns>
|
||||
public static TResult On<TSource, TResult>(
|
||||
this TSource value,
|
||||
Func<TSource, TResult> func)
|
||||
=> func(value);
|
||||
|
||||
/// <summary>
|
||||
/// Also:执行操作并返回原值
|
||||
/// </summary>
|
||||
@ -113,6 +112,4 @@ public static class PipeExtensions
|
||||
this TSource value,
|
||||
Func<TSource, TResult> transform)
|
||||
=> transform(value);
|
||||
|
||||
|
||||
}
|
||||
44
GFramework.Core/functional/types/NullableExtensions.cs
Normal file
44
GFramework.Core/functional/types/NullableExtensions.cs
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2026 GeWuYou
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
namespace GFramework.Core.functional.types;
|
||||
|
||||
/// <summary>
|
||||
/// 提供Nullable类型转换为Option类型的扩展方法
|
||||
/// </summary>
|
||||
public static class NullableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 将可空引用类型转换为Option类型
|
||||
/// </summary>
|
||||
/// <typeparam name="T">引用类型</typeparam>
|
||||
/// <param name="value">可空的引用类型值</param>
|
||||
/// <returns>如果值为null则返回None,否则返回包含该值的Some</returns>
|
||||
public static Option<T> ToOption<T>(this T? value)
|
||||
where T : class
|
||||
=> value is null
|
||||
? Option<T>.None()
|
||||
: Option<T>.Some(value);
|
||||
|
||||
/// <summary>
|
||||
/// 将可空值类型转换为Option类型
|
||||
/// </summary>
|
||||
/// <typeparam name="T">值类型</typeparam>
|
||||
/// <param name="value">可空的值类型值</param>
|
||||
/// <returns>如果值有值则返回包含该值的Some,否则返回None</returns>
|
||||
public static Option<T> ToOption<T>(this T? value)
|
||||
where T : struct
|
||||
=> value.HasValue
|
||||
? Option<T>.Some(value.Value)
|
||||
: Option<T>.None();
|
||||
}
|
||||
78
GFramework.Core/functional/types/Option.cs
Normal file
78
GFramework.Core/functional/types/Option.cs
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2026 GeWuYou
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
namespace GFramework.Core.functional.types;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个可能存在也可能不存在的值
|
||||
/// </summary>
|
||||
public readonly struct Option<T>
|
||||
{
|
||||
private readonly T _value;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前Option是否包含值
|
||||
/// </summary>
|
||||
public bool IsSome { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前Option是否为空值
|
||||
/// </summary>
|
||||
public bool IsNone => !IsSome;
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定值创建Option实例
|
||||
/// </summary>
|
||||
/// <param name="value">要包装的值</param>
|
||||
private Option(T value)
|
||||
{
|
||||
_value = value;
|
||||
IsSome = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建空的Option实例
|
||||
/// </summary>
|
||||
/// <param name="_">占位参数,用于区分构造函数重载</param>
|
||||
private Option(bool _)
|
||||
{
|
||||
_value = default!;
|
||||
IsSome = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建包含指定值的Option实例
|
||||
/// </summary>
|
||||
/// <param name="value">要包装的值,不能为null</param>
|
||||
/// <returns>包含指定值的Option实例</returns>
|
||||
/// <exception cref="ArgumentNullException">当value为null时抛出</exception>
|
||||
public static Option<T> Some(T value)
|
||||
{
|
||||
return value is null ? throw new ArgumentNullException(nameof(value)) : new Option<T>(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建空的Option实例
|
||||
/// </summary>
|
||||
/// <returns>空的Option实例</returns>
|
||||
public static Option<T> None() => new(false);
|
||||
|
||||
/// <summary>
|
||||
/// 获取Option中包含的值
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">当Option为空时抛出</exception>
|
||||
public T Value =>
|
||||
IsSome
|
||||
? _value
|
||||
: throw new InvalidOperationException("Option has no value");
|
||||
}
|
||||
89
GFramework.Core/functional/types/OptionExtensions.cs
Normal file
89
GFramework.Core/functional/types/OptionExtensions.cs
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2026 GeWuYou
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
namespace GFramework.Core.functional.types;
|
||||
|
||||
/// <summary>
|
||||
/// 提供Option类型的功能扩展方法,支持映射、绑定、过滤和匹配操作
|
||||
/// </summary>
|
||||
public static class OptionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 将Option中的值通过指定的映射函数转换为另一种类型的Option
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">源Option中值的类型</typeparam>
|
||||
/// <typeparam name="TResult">映射后结果的类型</typeparam>
|
||||
/// <param name="option">要进行映射操作的Option实例</param>
|
||||
/// <param name="mapper">用于将源值转换为目标值的映射函数</param>
|
||||
/// <returns>包含映射后值的新Option实例,如果原Option为空则返回None</returns>
|
||||
public static Option<TResult> Map<TSource, TResult>(
|
||||
this Option<TSource> option,
|
||||
Func<TSource, TResult> mapper)
|
||||
{
|
||||
return option.IsSome
|
||||
? Option<TResult>.Some(mapper(option.Value))
|
||||
: Option<TResult>.None();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将Option中的值通过指定的绑定函数转换为另一个Option,实现Option的扁平化映射
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">源Option中值的类型</typeparam>
|
||||
/// <typeparam name="TResult">绑定后Option中值的类型</typeparam>
|
||||
/// <param name="option">要进行绑定操作的Option实例</param>
|
||||
/// <param name="binder">用于将源值转换为新Option的绑定函数</param>
|
||||
/// <returns>绑定函数返回的Option实例,如果原Option为空则返回None</returns>
|
||||
public static Option<TResult> Bind<TSource, TResult>(
|
||||
this Option<TSource> option,
|
||||
Func<TSource, Option<TResult>> binder)
|
||||
{
|
||||
return option.IsSome
|
||||
? binder(option.Value)
|
||||
: Option<TResult>.None();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的谓词函数过滤Option中的值
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">Option中值的类型</typeparam>
|
||||
/// <param name="option">要进行过滤操作的Option实例</param>
|
||||
/// <param name="predicate">用于判断值是否满足条件的谓词函数</param>
|
||||
/// <returns>如果Option有值且满足谓词条件则返回原Option,否则返回None</returns>
|
||||
public static Option<TSource> Filter<TSource>(
|
||||
this Option<TSource> option,
|
||||
Func<TSource, bool> predicate)
|
||||
{
|
||||
return option.IsSome && predicate(option.Value)
|
||||
? option
|
||||
: Option<TSource>.None();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对Option进行模式匹配,根据Option的状态执行不同的函数
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">Option中值的类型</typeparam>
|
||||
/// <typeparam name="TResult">匹配结果的类型</typeparam>
|
||||
/// <param name="option">要进行匹配操作的Option实例</param>
|
||||
/// <param name="some">当Option包含值时执行的函数</param>
|
||||
/// <param name="none">当Option为空时执行的函数</param>
|
||||
/// <returns>根据Option状态执行相应函数后的结果</returns>
|
||||
public static TResult Match<TSource, TResult>(
|
||||
this Option<TSource> option,
|
||||
Func<TSource, TResult> some,
|
||||
Func<TResult> none)
|
||||
{
|
||||
return option.IsSome
|
||||
? some(option.Value)
|
||||
: none();
|
||||
}
|
||||
}
|
||||
56
GFramework.Core/functional/types/OptionValueExtensions.cs
Normal file
56
GFramework.Core/functional/types/OptionValueExtensions.cs
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2026 GeWuYou
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
namespace GFramework.Core.functional.types;
|
||||
|
||||
/// <summary>
|
||||
/// 提供Option类型值的操作扩展方法
|
||||
/// </summary>
|
||||
public static class OptionValueExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取Option中的值,如果Option为空则返回默认值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Option中存储的值的类型</typeparam>
|
||||
/// <param name="option">要获取值的Option对象</param>
|
||||
/// <param name="defaultValue">当Option为空时返回的默认值</param>
|
||||
/// <returns>Option中的值或默认值</returns>
|
||||
public static T GetOrElse<T>(
|
||||
this Option<T> option,
|
||||
T defaultValue)
|
||||
=> option.IsSome ? option.Value : defaultValue;
|
||||
|
||||
/// <summary>
|
||||
/// 获取Option中的值,如果Option为空则通过工厂函数生成默认值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Option中存储的值的类型</typeparam>
|
||||
/// <param name="option">要获取值的Option对象</param>
|
||||
/// <param name="defaultFactory">当Option为空时用于生成默认值的工厂函数</param>
|
||||
/// <returns>Option中的值或通过工厂函数生成的值</returns>
|
||||
public static T GetOrElse<T>(
|
||||
this Option<T> option,
|
||||
Func<T> defaultFactory)
|
||||
=> option.IsSome ? option.Value : defaultFactory();
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前Option,如果当前Option为空则返回备用Option
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Option中存储的值的类型</typeparam>
|
||||
/// <param name="option">当前Option对象</param>
|
||||
/// <param name="fallback">当当前Option为空时返回的备用Option</param>
|
||||
/// <returns>当前Option或备用Option</returns>
|
||||
public static Option<T> OrElse<T>(
|
||||
this Option<T> option,
|
||||
Option<T> fallback)
|
||||
=> option.IsSome ? option : fallback;
|
||||
}
|
||||
@ -17,4 +17,11 @@ namespace GFramework.Game.Abstractions.setting;
|
||||
/// 定义一个可重置且可应用设置的接口
|
||||
/// 该接口继承自IResettable和IApplyAbleSettings接口,组合了重置功能和应用设置功能
|
||||
/// </summary>
|
||||
public interface IResetApplyAbleSettings : IResettable, IApplyAbleSettings;
|
||||
public interface IResetApplyAbleSettings : IResettable, IApplyAbleSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取设置数据对象
|
||||
/// </summary>
|
||||
/// <returns>ISettingsData类型的设置数据</returns>
|
||||
ISettingsData Data { get; }
|
||||
}
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Collections.Concurrent;
|
||||
global using System.Linq;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
@ -1,5 +1,4 @@
|
||||
using System.Collections.Concurrent;
|
||||
using GFramework.Core.Abstractions.logging;
|
||||
using GFramework.Core.Abstractions.logging;
|
||||
using GFramework.Core.extensions;
|
||||
using GFramework.Core.logging;
|
||||
using GFramework.Core.model;
|
||||
|
||||
@ -34,6 +34,8 @@ public class GodotAudioSettings(ISettingsModel model, AudioBusMap audioBusMap)
|
||||
model.GetData<AudioSettings>().Reset();
|
||||
}
|
||||
|
||||
public ISettingsData Data { get; } = model.GetData<AudioSettings>();
|
||||
|
||||
/// <summary>
|
||||
/// 设置指定音频总线的音量
|
||||
/// </summary>
|
||||
|
||||
@ -50,4 +50,6 @@ public class GodotGraphicsSettings(ISettingsModel model) : IResetApplyAbleSettin
|
||||
{
|
||||
model.GetData<GraphicsSettings>().Reset();
|
||||
}
|
||||
|
||||
public ISettingsData Data { get; } = model.GetData<GraphicsSettings>();
|
||||
}
|
||||
@ -47,4 +47,6 @@ public class GodotLocalizationSettings(ISettingsModel model, LocalizationMap loc
|
||||
{
|
||||
model.GetData<LocalizationSettings>().Reset();
|
||||
}
|
||||
|
||||
public ISettingsData Data { get; } = model.GetData<LocalizationSettings>();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user