From bd0c9bbf4a3e806a18d77adbc5e88a38fc877fd7 Mon Sep 17 00:00:00 2001 From: sanchime Date: Fri, 7 Apr 2023 22:10:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E7=A4=BA=E4=BE=8B=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 35 +++++ ChipDemo.sln | 22 ++++ Core/Core.csproj | 14 ++ Core/Features/DefaultFeature.cs | 38 ++++++ Core/Features/Feature.cs | 43 ++++++ Core/Features/FeatureException.cs | 19 +++ Core/Features/FeatureExceptionContext.cs | 23 ++++ Core/Features/FeatureList.cs | 17 +++ Core/Features/FeatureWrapper.cs | 144 +++++++++++++++++++++ Core/Models/Chip.cs | 67 ++++++++++ Core/Models/Formula.cs | 12 ++ Core/Models/FormulaExecutionContext.cs | 6 + Core/Models/IMetadata.cs | 14 ++ Core/Models/MetadataProperty.cs | 110 ++++++++++++++++ Core/Primitives/FormulaExecutor.cs | 21 +++ Core/Primitives/FormulaExpressionParser.cs | 6 + Core/Primitives/IComputable.cs | 16 +++ Core/Primitives/IFormulaEngine.cs | 9 ++ Example/Example.csproj | 14 ++ Example/Program.cs | 41 ++++++ 20 files changed, 671 insertions(+) create mode 100644 .gitignore create mode 100644 ChipDemo.sln create mode 100644 Core/Core.csproj create mode 100644 Core/Features/DefaultFeature.cs create mode 100644 Core/Features/Feature.cs create mode 100644 Core/Features/FeatureException.cs create mode 100644 Core/Features/FeatureExceptionContext.cs create mode 100644 Core/Features/FeatureList.cs create mode 100644 Core/Features/FeatureWrapper.cs create mode 100644 Core/Models/Chip.cs create mode 100644 Core/Models/Formula.cs create mode 100644 Core/Models/FormulaExecutionContext.cs create mode 100644 Core/Models/IMetadata.cs create mode 100644 Core/Models/MetadataProperty.cs create mode 100644 Core/Primitives/FormulaExecutor.cs create mode 100644 Core/Primitives/FormulaExpressionParser.cs create mode 100644 Core/Primitives/IComputable.cs create mode 100644 Core/Primitives/IFormulaEngine.cs create mode 100644 Example/Example.csproj create mode 100644 Example/Program.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ff84679 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# 编译目录 +bin/ +obj/ +.idea/ +.vscode/ +.vs/ + +# Nuget包目录 +packages/ + + +# 编译结果 +[Dd]ebug/ +[Rr]elease/ +x64/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.log +*.vspscc +*.vssscc +.builds +.fake \ No newline at end of file diff --git a/ChipDemo.sln b/ChipDemo.sln new file mode 100644 index 0000000..3e5f7ab --- /dev/null +++ b/ChipDemo.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{E6270F58-996E-4DC2-8E18-34ECF4E222AB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{09918FD7-8BF2-4DAF-BAB9-1C97E04E458B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E6270F58-996E-4DC2-8E18-34ECF4E222AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6270F58-996E-4DC2-8E18-34ECF4E222AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6270F58-996E-4DC2-8E18-34ECF4E222AB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6270F58-996E-4DC2-8E18-34ECF4E222AB}.Release|Any CPU.Build.0 = Release|Any CPU + {09918FD7-8BF2-4DAF-BAB9-1C97E04E458B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {09918FD7-8BF2-4DAF-BAB9-1C97E04E458B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {09918FD7-8BF2-4DAF-BAB9-1C97E04E458B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {09918FD7-8BF2-4DAF-BAB9-1C97E04E458B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Core/Core.csproj b/Core/Core.csproj new file mode 100644 index 0000000..9f22194 --- /dev/null +++ b/Core/Core.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + Demo + + + + + + + diff --git a/Core/Features/DefaultFeature.cs b/Core/Features/DefaultFeature.cs new file mode 100644 index 0000000..b1f5bcc --- /dev/null +++ b/Core/Features/DefaultFeature.cs @@ -0,0 +1,38 @@ +using Demo.Models; + +namespace Demo.Features; + +/// +/// 默认的功能类,做委托容器用 +/// +/// +public sealed class DefaultFeature : Feature + where TMetadata : IMetadata +{ + public DefaultFeature(string name, FeatureFunction? executeFunction = null, + FeatureFunctionAsync? executeFunctionAsync = null, + int order = 1) + { + _executeFunction = executeFunction; + _executeFunctionAsync = executeFunctionAsync; + Name = name; + Order = order; + } + + private readonly FeatureFunction? _executeFunction; + + private readonly FeatureFunctionAsync? _executeFunctionAsync; + + public override void Execute(TMetadata metadata) + { + _executeFunction?.Invoke(this, metadata); + } + + public override async ValueTask ExecuteAsync(TMetadata metadata) + { + if (_executeFunctionAsync is not null) + { + await _executeFunctionAsync.Invoke(this, metadata); + } + } +} \ No newline at end of file diff --git a/Core/Features/Feature.cs b/Core/Features/Feature.cs new file mode 100644 index 0000000..d107981 --- /dev/null +++ b/Core/Features/Feature.cs @@ -0,0 +1,43 @@ +using Demo.Models; + +namespace Demo.Features; + +/// +/// 抽象功能类 +/// +public abstract class Feature + where TMetadata : IMetadata +{ + /// + /// 执行顺序 + /// + public int Order { get; init; } + + public bool Finished { get; protected set; } + + public string Name { get; init; } = String.Empty; + + public abstract void Execute(TMetadata metadata); + + public virtual ValueTask ExecuteAsync(TMetadata metadata) + { + Execute(metadata); + return ValueTask.CompletedTask; + } + + public virtual void OnException(FeatureExceptionContext context) + { + throw context.Exception; + } + + public virtual ValueTask OnExceptionAsync(FeatureExceptionContext context) + { + throw context.Exception; + } +} + +public delegate void FeatureFunction(Feature feature, TMetadata metadata) + where TMetadata : IMetadata; + +public delegate ValueTask FeatureFunctionAsync(Feature feature, TMetadata metadata) + where TMetadata : IMetadata; \ No newline at end of file diff --git a/Core/Features/FeatureException.cs b/Core/Features/FeatureException.cs new file mode 100644 index 0000000..7542381 --- /dev/null +++ b/Core/Features/FeatureException.cs @@ -0,0 +1,19 @@ +using Demo.Models; + +namespace Demo.Features; + +public class FeatureException : Exception + where TMetadata : IMetadata +{ + public TMetadata Metadata { get; } + public Feature Feature { get; } + + private Exception _innerException; + + public FeatureException(TMetadata metadata, Feature feature, Exception ex) : base(ex.Message) + { + Metadata = metadata; + Feature = feature; + _innerException = ex; + } +} \ No newline at end of file diff --git a/Core/Features/FeatureExceptionContext.cs b/Core/Features/FeatureExceptionContext.cs new file mode 100644 index 0000000..762c76d --- /dev/null +++ b/Core/Features/FeatureExceptionContext.cs @@ -0,0 +1,23 @@ +using Demo.Models; + +namespace Demo.Features; + +/// +/// 功能运行时异常上下文 +/// +public class FeatureExceptionContext + where TMetadata : IMetadata +{ + public Feature Feature { get; } + public FeatureException Exception { get; } + public FeatureExceptionContext(Feature feature, FeatureException exception) + { + Feature = feature; + Exception = exception; + } + + /// + /// 表示异常是否已处理 + /// + public bool Handled { get; set; } = false; +} \ No newline at end of file diff --git a/Core/Features/FeatureList.cs b/Core/Features/FeatureList.cs new file mode 100644 index 0000000..8d9f7a9 --- /dev/null +++ b/Core/Features/FeatureList.cs @@ -0,0 +1,17 @@ +using System.Collections.ObjectModel; +using Demo.Models; + +namespace Demo.Features; + +/// +/// 功能列表容器 +/// +/// +public class FeatureList : Dictionary> + where TMetadata : IMetadata +{ + public FeatureList(IDictionary>? list = null) + : base(list ?? new Dictionary>()) + { + } +} \ No newline at end of file diff --git a/Core/Features/FeatureWrapper.cs b/Core/Features/FeatureWrapper.cs new file mode 100644 index 0000000..4579a87 --- /dev/null +++ b/Core/Features/FeatureWrapper.cs @@ -0,0 +1,144 @@ +using System.ComponentModel.Design; +using Demo.Models; + +namespace Demo.Features; + +public class FeatureWrapper + where TMetadata : IMetadata +{ + private readonly TMetadata _metadata; + + protected FeatureList Features { get; } + + public FeatureWrapper(TMetadata metadata, FeatureList? features = null) + { + _metadata = metadata; + Features = features ?? new FeatureList(); + } + + public FeatureWrapper DropFeature(string name) + { + if (String.IsNullOrWhiteSpace(name)) + { + throw new CheckoutException("功能名称不能为空或空白字符"); + } + Features.Remove(name); + + return this; + } + + /// + /// 添加功能 + /// + /// + /// + /// + public FeatureWrapper WithFeature(TFeature feature) + where TFeature : Feature + { + if (feature is null) + { + return this; + } + + if (String.IsNullOrWhiteSpace(feature.Name)) + { + throw new CheckoutException("功能名称不能为空或空白字符"); + } + Features.TryAdd(feature.Name, feature); + + return this; + } + + /// + /// 添加功能 + /// + /// + /// + /// + /// + public FeatureWrapper WithFeature(string name, FeatureFunction feature, int order = 1) + { + if (String.IsNullOrWhiteSpace(name)) + { + throw new CheckoutException("功能名称不能为空或空白字符"); + } + if (feature is not null) + { + Features.TryAdd(name, new DefaultFeature(name, feature, order: order)); + } + + return this; + } + + /// + /// 添加功能 + /// + /// + /// + /// + /// + public FeatureWrapper WithFeature(string name, FeatureFunctionAsync feature, int order = 1) + { + if (String.IsNullOrWhiteSpace(name)) + { + throw new CheckoutException("功能名称不能为空或空白字符"); + } + if (feature is not null) + { + Features.TryAdd(name, new DefaultFeature(name, executeFunctionAsync: feature, order: order)); + } + + return this; + } + + public void Execute() + { + foreach (var feature in Features.Values.OrderBy(f => f.Order)) + { + try + { + feature.Execute(_metadata); + if (feature.Finished) + { + break; + } + } + catch (Exception ex) + { + feature.OnException(new FeatureExceptionContext(feature, new FeatureException(_metadata, feature, ex))); + } + } + } + + public void Execute(string name) + { + if (Features.TryGetValue(name, out var feature)) + { + feature.Execute(_metadata); + } + else + { + throw new KeyNotFoundException(name); + } + } + + public async ValueTask ExecuteAsync() + { + foreach (var feature in Features.Values.OrderBy(f => f.Order)) + { + try + { + await feature.ExecuteAsync(_metadata); + if (feature.Finished) + { + break; + } + } + catch (Exception ex) + { + await feature.OnExceptionAsync(new FeatureExceptionContext(feature, new FeatureException(_metadata, feature, ex))); + } + } + } +} \ No newline at end of file diff --git a/Core/Models/Chip.cs b/Core/Models/Chip.cs new file mode 100644 index 0000000..e6fc154 --- /dev/null +++ b/Core/Models/Chip.cs @@ -0,0 +1,67 @@ +using Demo.Features; +using Demo.Primitives; + +namespace Demo.Models; + +public abstract class Chip : IMetadata +{ + /// + /// 属性 + /// + public MetadataPropertySet Properties { get; } + + public string Name { get; set; } + + public Version Version { get; set; } = new Version(0, 0, 0); + + /// + /// 功能 + /// + public virtual FeatureWrapper Features { get; } + + /// + /// 公式 + /// + public FormulaExecutor Formula { get; protected set; } + + protected Chip(Chip? @base = null, bool needInherit = true) + { + if (needInherit) + { + Properties = @base?.Properties ?? new MetadataPropertySet(); + } + else + { + Properties = new MetadataPropertySet(); + } + Features = new FeatureWrapper(this); + Formula = new FormulaExecutor(null, null); + Initialize(); + } + + public MetadataPropertySet.MetadataProperty? this[string name] + { + get => Properties.GetValue(name); + set => Properties.SetValue(name, value); + } + + + public Chip SetProperty(string name, object? value) + { + Properties.SetValue(name, value); + return this; + } + + public TValue? GetProperty(string name) + { + return Properties.GetValue(name).Cast(); + } + + /// + /// 初始化 + /// + protected virtual void Initialize() + { + // var properties = this.GetType().GetProperties(); + } +} \ No newline at end of file diff --git a/Core/Models/Formula.cs b/Core/Models/Formula.cs new file mode 100644 index 0000000..33841d0 --- /dev/null +++ b/Core/Models/Formula.cs @@ -0,0 +1,12 @@ +namespace Demo.Models; + +/// +/// 公式模型类 +/// +public class Formula +{ + /// + /// 是否启用 + /// + public bool Enabled { get; set; } = true; +} \ No newline at end of file diff --git a/Core/Models/FormulaExecutionContext.cs b/Core/Models/FormulaExecutionContext.cs new file mode 100644 index 0000000..9fdf5e1 --- /dev/null +++ b/Core/Models/FormulaExecutionContext.cs @@ -0,0 +1,6 @@ +namespace Demo.Models; + +public class FormulaExecutionContext +{ + +} \ No newline at end of file diff --git a/Core/Models/IMetadata.cs b/Core/Models/IMetadata.cs new file mode 100644 index 0000000..e41097e --- /dev/null +++ b/Core/Models/IMetadata.cs @@ -0,0 +1,14 @@ +using Demo.Features; + +namespace Demo.Models; + +/// +/// 元数据 +/// +public interface IMetadata +{ + /// + /// 属性 + /// + MetadataPropertySet Properties { get; } +} \ No newline at end of file diff --git a/Core/Models/MetadataProperty.cs b/Core/Models/MetadataProperty.cs new file mode 100644 index 0000000..7b113fa --- /dev/null +++ b/Core/Models/MetadataProperty.cs @@ -0,0 +1,110 @@ +using System.Collections; +using System.Collections.Concurrent; + +namespace Demo.Models; + +public class MetadataPropertySet : IReadOnlyDictionary +{ + private readonly IDictionary _properties; + + public MetadataPropertySet() + { + _properties = new Dictionary(); + } + + public bool ContainsKey(string key) + { + return _properties.ContainsKey(key); + } + + public bool TryGetValue(string key, out MetadataProperty value) + { + return _properties.TryGetValue(key, out value); + } + + public IEnumerable Keys => _properties.Keys; + public IEnumerable Values => _properties.Values; + + public MetadataProperty? this[string name] + { + get => GetValue(name); + } + + internal void SetValue(string name, object value) + { + if (_properties.TryGetValue(name, out var property)) + { + property.Value = value; + } + else + { + property = new MetadataProperty(name, value); + _properties.TryAdd(name, property); + } + } + + internal MetadataProperty? GetValue(string name) + { + if (_properties.TryGetValue(name, out var property)) + { + return property; + } + + return null; + } + + public class MetadataProperty + { + public bool IsNull { get; private set; } + public Type? Type { get; private set; } + + private object? _value; + /// + /// 属性值 + /// + public object? Value + { + get => _value; + set + { + IsNull = value is null; + Type = value?.GetType(); + _value = value; + } + } + + public string Name { get; init; } + + public TValue? Cast() + { + if (typeof(TValue) == Type) + { + return (TValue)Convert.ChangeType(Value, Type); + } + + return (TValue)Value; + } + + public MetadataProperty(string name, object? value) + { + (Name, Value) = (name, value); + } + + public override string ToString() + { + return Value?.ToString()!; + } + } + + public IEnumerator> GetEnumerator() + { + return _properties.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public int Count => _properties.Count; +} \ No newline at end of file diff --git a/Core/Primitives/FormulaExecutor.cs b/Core/Primitives/FormulaExecutor.cs new file mode 100644 index 0000000..d09ecfb --- /dev/null +++ b/Core/Primitives/FormulaExecutor.cs @@ -0,0 +1,21 @@ +using Demo.Models; + +namespace Demo.Primitives; + +public class FormulaExecutor : IComputable +{ + private IFormulaEngine _engine; + private FormulaExpressionParser _parser; + + public FormulaExecutor(IFormulaEngine engine, FormulaExpressionParser parser) + { + _engine = engine; + _parser = parser; + } + + public decimal Compute(FormulaExecutionContext context) + { + Console.WriteLine("计算过程"); + return default; + } +} \ No newline at end of file diff --git a/Core/Primitives/FormulaExpressionParser.cs b/Core/Primitives/FormulaExpressionParser.cs new file mode 100644 index 0000000..23545fe --- /dev/null +++ b/Core/Primitives/FormulaExpressionParser.cs @@ -0,0 +1,6 @@ +namespace Demo.Primitives; + +public class FormulaExpressionParser +{ + +} \ No newline at end of file diff --git a/Core/Primitives/IComputable.cs b/Core/Primitives/IComputable.cs new file mode 100644 index 0000000..c6551d7 --- /dev/null +++ b/Core/Primitives/IComputable.cs @@ -0,0 +1,16 @@ +namespace Demo.Primitives; + +/// +/// 可计算单元 +/// +public interface IComputable +{ + /// + /// 计算 + /// + /// + /// + TResult Compute(TInput input); + + // TResult Compute(Func computor); +} \ No newline at end of file diff --git a/Core/Primitives/IFormulaEngine.cs b/Core/Primitives/IFormulaEngine.cs new file mode 100644 index 0000000..2af2412 --- /dev/null +++ b/Core/Primitives/IFormulaEngine.cs @@ -0,0 +1,9 @@ +namespace Demo.Primitives; + +/// +/// 公式引擎 +/// +public interface IFormulaEngine +{ + +} \ No newline at end of file diff --git a/Example/Example.csproj b/Example/Example.csproj new file mode 100644 index 0000000..c0cde45 --- /dev/null +++ b/Example/Example.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/Example/Program.cs b/Example/Program.cs new file mode 100644 index 0000000..d94c4f5 --- /dev/null +++ b/Example/Program.cs @@ -0,0 +1,41 @@ +using Demo.Models; + +try +{ + IcndChip icndChip = new IcndChip(); + icndChip.SetProperty("Code", "1234") + .SetProperty("Name", "芯片"); + icndChip.Features.Execute(); // 执行所有功能 + + icndChip.Features + .DropFeature("场频自适应功能") + .Execute(); + + icndChip.Features.Execute("场频自适应功能"); + + icndChip.Formula.Compute(new FormulaExecutionContext() // 计算公式(需要调整) + { + + }); + + Console.WriteLine(icndChip.GetProperty("Code")); +} +catch (Exception ex) +{ + Console.WriteLine(ex); +} +public class IcndChip : Chip +{ + protected override void Initialize() + { + Name = "ICND芯片"; + Features.WithFeature("场频自适应功能", (f, m) => + { + Console.WriteLine($"{m.Name}调用{f.Name}"); + }).WithFeature("其他功能", (f, m) => + { + Console.WriteLine($"{m.Name}调用{f.Name},得到属性[\"Code\"]值{m.GetProperty("Code")}"); + }); + } +} +