示例工程版本提升至net462,项目添加了部分空引用检测逻辑。累了,消不完的空引用警告(T.T)

This commit is contained in:
fengjiayi
2024-09-30 22:20:02 +08:00
parent 8ecbdfa7a6
commit e4aa3b6185
45 changed files with 562 additions and 581 deletions

View File

@@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Serein.Library.Framework</RootNamespace>
<AssemblyName>Serein.Library.Framework</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
</PropertyGroup>

View File

@@ -7,196 +7,193 @@ using System.Threading.Tasks;
namespace Serein.Library.Utils
{
/// <summary>
/// 流程中断管理
/// </summary>
public class ChannelFlowInterrupt
{
/// <summary>
/// 中断取消类型
/// </summary>
public enum CancelType
{
Manual,
Error,
Overtime
}
// 使用并发字典管理每个信号对应的 Channel
private readonly ConcurrentDictionary<string, Channel<CancelType>> _channels = new ConcurrentDictionary<string, Channel<CancelType>>();
/// <summary>
/// 创建信号并指定超时时间,到期后自动触发(异步方法)
/// </summary>
/// <param name="signal">信号标识符</param>
/// <param name="outTime">超时时间</param>
/// <returns>等待任务</returns>
public async Task<CancelType> GetCreateChannelWithTimeoutAsync(string signal, TimeSpan outTime)
{
var channel = GetOrCreateChannel(signal);
var cts = new CancellationTokenSource();
// 异步任务:超时后自动触发信号
_ = Task.Run(async () =>
/// <summary>
/// 流程中断管理
/// </summary>
public class ChannelFlowInterrupt
{
try
/// <summary>
/// 中断取消类型
/// </summary>
public enum CancelType
{
await Task.Delay(outTime, cts.Token);
if (!cts.Token.IsCancellationRequested)
Manual,
Error,
Overtime
}
// 使用并发字典管理每个信号对应的 Channel
private readonly ConcurrentDictionary<string, Channel<CancelType>> _channels = new ConcurrentDictionary<string, Channel<CancelType>>();
/// <summary>
/// 创建信号并指定超时时间,到期后自动触发(异步方法)
/// </summary>
/// <param name="signal">信号标识符</param>
/// <param name="outTime">超时时间</param>
/// <returns>等待任务</returns>
public async Task<CancelType> GetCreateChannelWithTimeoutAsync(string signal, TimeSpan outTime)
{
var channel = GetOrCreateChannel(signal);
var cts = new CancellationTokenSource();
// 异步任务:超时后自动触发信号
_ = Task.Run(async () =>
{
await channel.Writer.WriteAsync(CancelType.Overtime);
try
{
await Task.Delay(outTime, cts.Token);
if (!cts.Token.IsCancellationRequested)
{
await channel.Writer.WriteAsync(CancelType.Overtime);
}
}
catch (OperationCanceledException)
{
// 超时任务被取消
}
finally
{
cts?.Dispose();
}
}, cts.Token);
// 等待信号传入(超时或手动触发)
try
{
var result = await channel.Reader.ReadAsync();
return result;
}
catch
{
return CancelType.Error;
}
}
catch (OperationCanceledException)
{
// 超时任务被取消
}
finally
{
cts?.Dispose();
}
}, cts.Token);
// 等待信号传入(超时或手动触发)
try
{
var result = await channel.Reader.ReadAsync();
return result;
}
catch
{
return CancelType.Error;
}
}
/// <summary>
/// 创建信号,直到手动触发(异步方法)
/// </summary>
/// <param name="signal">信号标识符</param>
/// <param name="outTime">超时时间</param>
/// <returns>等待任务</returns>
public async Task<CancelType> GetOrCreateChannelAsync(string signal)
{
try
{
var channel = GetOrCreateChannel(signal);
// 等待信号传入(超时或手动触发)
var result = await channel.Reader.ReadAsync();
return result;
}
catch
{
return CancelType.Manual;
}
}
/// <summary>
/// 创建信号并指定超时时间,到期后自动触发(同步阻塞方法)
/// </summary>
/// <param name="signal">信号标识符</param>
/// <param name="timeout">超时时间</param>
public CancelType CreateChannelWithTimeoutSync(string signal, TimeSpan timeout)
{
var channel = GetOrCreateChannel(signal);
var cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// 异步任务:超时后自动触发信号
_ = Task.Run(async () =>
/// <summary>
/// 创建信号,直到手动触发(异步方法)
/// </summary>
/// <param name="signal">信号标识符</param>
/// <param name="outTime">超时时间</param>
/// <returns>等待任务</returns>
public async Task<CancelType> GetOrCreateChannelAsync(string signal)
{
try
{
await Task.Delay(timeout, token);
await channel.Writer.WriteAsync(CancelType.Overtime);
var channel = GetOrCreateChannel(signal);
// 等待信号传入(超时或手动触发)
var result = await channel.Reader.ReadAsync();
return result;
}
catch (OperationCanceledException ex)
catch
{
// 任务被取消
await Console.Out.WriteLineAsync(ex.Message);
return CancelType.Manual;
}
});
// 同步阻塞直到信号触发或超时
var result = channel.Reader.ReadAsync().AsTask().GetAwaiter().GetResult();
return result;
}
/// <summary>
/// 触发信号
/// </summary>
/// <param name="signal">信号字符串</param>
/// <returns>是否成功触发</returns>
public bool TriggerSignal(string signal)
{
//if (_channels.TryGetValue(signal, out var channel))
//{
// // 手动触发信号
// channel.Writer.TryWrite(CancelType.Manual);
// return true;
//}
//return false;
try
{
if (_channels.TryGetValue(signal, out var channel))
{
// 手动触发信号
channel.Writer.TryWrite(CancelType.Manual);
// 完成写入,标记该信号通道不再接受新写入
channel.Writer.Complete();
// 触发后移除信号
_channels.TryRemove(signal, out _);
return true;
}
return false;
}
catch
{
return false;
}
}
/// <summary>
/// 取消所有任务
/// </summary>
public void CancelAllTasks()
{
foreach (var channel in _channels.Values)
{
try
{
channel.Writer.Complete();
}
finally
/// <summary>
/// 创建信号并指定超时时间,到期后自动触发(同步阻塞方法)
/// </summary>
/// <param name="signal">信号标识符</param>
/// <param name="timeout">超时时间</param>
public CancelType CreateChannelWithTimeoutSync(string signal, TimeSpan timeout)
{
var channel = GetOrCreateChannel(signal);
var cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// 异步任务:超时后自动触发信号
_ = Task.Run(async () =>
{
try
{
await Task.Delay(timeout, token);
await channel.Writer.WriteAsync(CancelType.Overtime);
}
catch (OperationCanceledException ex)
{
// 任务被取消
await Console.Out.WriteLineAsync(ex.Message);
}
});
// 同步阻塞直到信号触发或超时
var result = channel.Reader.ReadAsync().AsTask().GetAwaiter().GetResult();
return result;
}
}
_channels.Clear();
}
/// <summary>
/// 获取或创建指定信号的 Channel
/// </summary>
/// <param name="signal">信号字符串</param>
/// <returns>对应的 Channel</returns>
private Channel<CancelType> GetOrCreateChannel(string signal)
{
return _channels.GetOrAdd(signal, _ => Channel.CreateUnbounded<CancelType>());
}
}
/// <summary>
/// 触发信号
/// </summary>
/// <param name="signal">信号字符串</param>
/// <returns>是否成功触发</returns>
public bool TriggerSignal(string signal)
{
//if (_channels.TryGetValue(signal, out var channel))
//{
// // 手动触发信号
// channel.Writer.TryWrite(CancelType.Manual);
// return true;
//}
//return false;
try
{
if (_channels.TryGetValue(signal, out var channel))
{
// 手动触发信号
channel.Writer.TryWrite(CancelType.Manual);
// 完成写入,标记该信号通道不再接受新写入
channel.Writer.Complete();
// 触发后移除信号
_channels.TryRemove(signal, out _);
return true;
}
return false;
}
catch
{
return false;
}
}
/// <summary>
/// 取消所有任务
/// </summary>
public void CancelAllTasks()
{
foreach (var channel in _channels.Values)
{
try
{
channel.Writer.Complete();
}
finally
{
}
}
_channels.Clear();
}
/// <summary>
/// 获取或创建指定信号的 Channel
/// </summary>
/// <param name="signal">信号字符串</param>
/// <returns>对应的 Channel</returns>
private Channel<CancelType> GetOrCreateChannel(string signal)
{
return _channels.GetOrAdd(signal, _ => Channel.CreateUnbounded<CancelType>());
}
}
}
#endregion

View File

@@ -345,6 +345,7 @@ namespace Serein.Library.Web
}
catch (JsonReaderException ex)
{
Console.WriteLine(ex);
return value;
}
catch (JsonSerializationException ex)

View File

@@ -1,4 +1,4 @@
using Net461DllTest.LogicControl;
using Net462DllTest.LogicControl;
using Serein.Library.Attributes;
using Serein.Library.NodeFlow.Tool;
using System;
@@ -7,7 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Net461DllTest.Device
namespace Net462DllTest.Device
{
[AutoRegister]
public class PrakingDevice : ChannelFlowTrigger<ParkingCommand>

View File

@@ -1,13 +1,13 @@
using IoTClient;
using IoTClient.Clients.PLC;
using IoTClient.Enums;
using Net461DllTest.Enums;
using Net461DllTest.Signal;
using Net462DllTest.Enums;
using Net462DllTest.Signal;
using Serein.Library.Attributes;
using Serein.Library.NodeFlow.Tool;
using System;
namespace Net461DllTest.Device
namespace Net462DllTest.Device
{
/// <summary>

View File

@@ -1,4 +1,4 @@
using Net461DllTest.View;
using Net462DllTest.View;
using Serein.Library.Attributes;
using System;
using System.Collections.Generic;
@@ -6,7 +6,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Net461DllTest.Signal
namespace Net462DllTest.Signal
{
public enum FromValue
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Net461DllTest.Enums
namespace Net462DllTest.Enums
{
public enum PlcState
{

View File

@@ -1,12 +1,12 @@
using Net461DllTest.Signal;
using Net462DllTest.Signal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Net461DllTest.Signal.PlcValueAttribute;
using static Net462DllTest.Signal.PlcValueAttribute;
namespace Net461DllTest.Enums
namespace Net462DllTest.Enums
{

View File

@@ -1,6 +1,6 @@
using Net461DllTest.Device;
using Net461DllTest.Signal;
using Net461DllTest.ViewModel;
using Net462DllTest.Device;
using Net462DllTest.Signal;
using Net462DllTest.ViewModel;
using Serein.Library.Api;
using Serein.Library.Attributes;
using Serein.Library.Enums;
@@ -13,7 +13,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Net461DllTest.LogicControl
namespace Net462DllTest.LogicControl
{
public enum ParkingCommand

View File

@@ -1,9 +1,9 @@
using IoTClient.Clients.PLC;
using IoTClient.Common.Enums;
using Net461DllTest.Device;
using Net461DllTest.Enums;
using Net461DllTest.Signal;
using Net461DllTest.Web;
using Net462DllTest.Device;
using Net462DllTest.Enums;
using Net462DllTest.Signal;
using Net462DllTest.Web;
using Serein.Library.Api;
using Serein.Library.Attributes;
using Serein.Library.Enums;
@@ -18,7 +18,7 @@ using System.ComponentModel;
using System.Reflection;
using System.Threading.Tasks;
namespace Net461DllTest.LogicControl
namespace Net462DllTest.LogicControl
{
[AutoRegister]
[DynamicFlow]

View File

@@ -1,6 +1,6 @@
using Net461DllTest.Device;
using Net461DllTest.Signal;
using Net461DllTest.ViewModel;
using Net462DllTest.Device;
using Net462DllTest.Signal;
using Net462DllTest.ViewModel;
using Serein.Library.Api;
using Serein.Library.Attributes;
using Serein.Library.Enums;
@@ -10,7 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace Net461DllTest.LogicControl
namespace Net462DllTest.LogicControl
{
/// <summary>

View File

@@ -6,9 +6,9 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E40EE629-1A38-4011-88E3-9AD036869987}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Net461DllTest</RootNamespace>
<AssemblyName>Net461DllTest</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<RootNamespace>Net462DllTest</RootNamespace>
<AssemblyName>Net462DllTest</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>

View File

@@ -1,4 +1,4 @@
namespace Net461DllTest.Signal
namespace Net462DllTest.Signal
{
public enum CommandSignal
{

View File

@@ -1,8 +1,8 @@
using Serein.Library.Attributes;
using System;
using static Net461DllTest.Signal.PlcValueAttribute;
using static Net462DllTest.Signal.PlcValueAttribute;
namespace Net461DllTest.Signal
namespace Net462DllTest.Signal
{
[AttributeUsage(AttributeTargets.Field)]

View File

@@ -1,7 +1,7 @@
using IoTClient;
using IoTClient.Clients.PLC;
using IoTClient.Enums;
using Net461DllTest.Signal;
using Net462DllTest.Signal;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -9,7 +9,7 @@ using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Net461DllTest.Utils
namespace Net462DllTest.Utils
{
internal static class MyPlcExtension
{

View File

@@ -1,4 +1,4 @@
namespace Net461DllTest
namespace Net462DllTest
{
partial class FromWorkBenchView
{

View File

@@ -1,6 +1,6 @@
using Net461DllTest.Device;
using Net461DllTest.Signal;
using Net461DllTest.ViewModel;
using Net462DllTest.Device;
using Net462DllTest.Signal;
using Net462DllTest.ViewModel;
using Serein.Library.Api;
using Serein.Library.Attributes;
using System;
@@ -13,7 +13,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Net461DllTest
namespace Net462DllTest
{
public partial class FromWorkBenchView : Form
{

View File

@@ -1,6 +1,6 @@
using System;
namespace Net461DllTest.View
namespace Net462DllTest.View
{
partial class TestFormView
{

View File

@@ -1,7 +1,7 @@
using System;
using System.Windows.Forms;
namespace Net461DllTest.View
namespace Net462DllTest.View
{
public partial class TestFormView : Form
{

View File

@@ -1,5 +1,5 @@
using Net461DllTest.Device;
using Net461DllTest.Signal;
using Net462DllTest.Device;
using Net462DllTest.Signal;
using Serein.Library.Attributes;
using System;
using System.Collections.Generic;
@@ -7,7 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Net461DllTest.ViewModel
namespace Net462DllTest.ViewModel
{
public class FromWorkBenchViewModel
{

View File

@@ -1,5 +1,5 @@
using Net461DllTest.Device;
using Net461DllTest.Signal;
using Net462DllTest.Device;
using Net462DllTest.Signal;
using Serein.Library.Attributes;
using Serein.Library.Web;
using System;
@@ -8,7 +8,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Net461DllTest.Web
namespace Net462DllTest.Web
{
[AutoHosting]
public class CommandController : ControllerBase

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO.Ports" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
<assemblyIdentity name="System.IO.Ports" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/></startup></configuration>

View File

@@ -41,12 +41,12 @@ namespace Serein.NodeFlow.Base
/// <summary>
/// 节点guid
/// </summary>
public string Guid { get; set; }
public string Guid { get; set; } = string.Empty;
/// <summary>
/// 显示名称
/// </summary>
public string DisplayName { get; set; }
public string DisplayName { get; set; } = string.Empty;
/// <summary>
/// 是否为起点控件
@@ -76,7 +76,7 @@ namespace Serein.NodeFlow.Base
/// <summary>
/// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
/// </summary>
public Exception RuningException { get; set; } = null;
public Exception? RuningException { get; set; } = null;
/// <summary>

View File

@@ -74,17 +74,17 @@ namespace Serein.NodeFlow.Base
internal virtual NodeModelBase LoadInfo(NodeInfo nodeInfo)
{
var node = this;
if (node != null)
this.Guid = nodeInfo.Guid;
if(this.MethodDetails is not null)
{
node.Guid = nodeInfo.Guid;
for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
{
Parameterdata? pd = nodeInfo.ParameterData[i];
node.MethodDetails.ExplicitDatas[i].IsExplicitData = pd.State;
node.MethodDetails.ExplicitDatas[i].DataValue = pd.Value;
this.MethodDetails.ExplicitDatas[i].IsExplicitData = pd.State;
this.MethodDetails.ExplicitDatas[i].DataValue = pd.Value;
}
}
return this;
}
@@ -132,7 +132,7 @@ namespace Serein.NodeFlow.Base
if (upstreamNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前
{
var cancelType = await upstreamNodes[i].DebugSetting.GetInterruptTask();
await Console.Out.WriteLineAsync($"[{upstreamNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支");
await Console.Out.WriteLineAsync($"[{upstreamNodes[i]?.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
}
upstreamNodes[i].PreviousNode = currentNode;
await upstreamNodes[i].StartExecute(context); // 执行流程节点的上游分支
@@ -165,7 +165,7 @@ namespace Serein.NodeFlow.Base
if (nextNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前
{
var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask();
await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支");
await Console.Out.WriteLineAsync($"[{nextNodes[i]?.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
}
nextNodes[i].PreviousNode = currentNode;
stack.Push(nextNodes[i]);
@@ -194,18 +194,22 @@ namespace Serein.NodeFlow.Base
// this.NextOrientation = ConnectionType.None;
// return null;
//}
await Console.Out.WriteLineAsync($"[{this.MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支");
await Console.Out.WriteLineAsync($"[{this.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
}
#endregion
MethodDetails md = MethodDetails;
MethodDetails? md = MethodDetails;
//var del = md.MethodDelegate.Clone();
if (md is null)
{
throw new Exception($"节点{this.Guid}不存在方法信息请检查是否需要重写节点的ExecutingAsync");
}
if (!context.Env.TryGetDelegate(md.MethodName, out var del))
{
throw new Exception("不存在对应委托");
throw new Exception($"节点{this.Guid}不存在对应委托");
}
md.ActingInstance ??= context.Env.IOC.Get(MethodDetails.ActingInstanceType);
md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType);
object instance = md.ActingInstance;
var haveParameter = md.ExplicitDatas.Length > 0;
@@ -281,7 +285,7 @@ namespace Serein.NodeFlow.Base
if (ed.IsExplicitData) // 判断是否使用显示的输入参数
{
if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase))
if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase) && flowData is not null)
{
// 执行表达式从上一节点获取对象
inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, flowData, out _);
@@ -315,8 +319,6 @@ namespace Serein.NodeFlow.Base
}
}
//var attribute = ed.DataType.GetCustomAttribute<EnumTypeConvertorAttribute>();
//if (attribute is not null && attribute.EnumType.IsEnum) // 获取枚举转换器中记录的枚举
if ( ed.DataType != ed.ExplicitType) // 获取枚举转换器中记录的枚举
{
if (ed.ExplicitType.IsEnum && Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum)) // 获取对应的枚举项
@@ -339,41 +341,43 @@ namespace Serein.NodeFlow.Base
try
{
string? valueStr = inputParameter?.ToString();
parameters[i] = ed.DataType switch
{
//Type t when t == previousDataType => inputParameter, // 上下文
Type t when t.IsEnum => Enum.Parse(ed.DataType, ed.DataValue),// 需要枚举
Type t when t == typeof(IDynamicContext) => context, // 上下文
Type t when t.IsEnum => Enum.Parse(ed.DataType, ed.DataValue),// 需要枚举
Type t when t == typeof(string) => inputParameter?.ToString(),
Type t when t == typeof(char) && !string.IsNullOrEmpty(valueStr) => char.Parse(valueStr),
Type t when t == typeof(bool) && !string.IsNullOrEmpty(valueStr) => inputParameter is not null && bool.Parse(valueStr),
Type t when t == typeof(float) && !string.IsNullOrEmpty(valueStr) => float.Parse(valueStr),
Type t when t == typeof(decimal) && !string.IsNullOrEmpty(valueStr) => decimal.Parse(valueStr),
Type t when t == typeof(double) && !string.IsNullOrEmpty(valueStr) => double.Parse(valueStr),
Type t when t == typeof(sbyte) && !string.IsNullOrEmpty(valueStr) => sbyte.Parse(valueStr),
Type t when t == typeof(byte) && !string.IsNullOrEmpty(valueStr) => byte.Parse(valueStr),
Type t when t == typeof(short) && !string.IsNullOrEmpty(valueStr) => short.Parse(valueStr),
Type t when t == typeof(ushort) && !string.IsNullOrEmpty(valueStr) => ushort.Parse(valueStr),
Type t when t == typeof(int) && !string.IsNullOrEmpty(valueStr) => int.Parse(valueStr),
Type t when t == typeof(uint) && !string.IsNullOrEmpty(valueStr) => uint.Parse(valueStr),
Type t when t == typeof(long) && !string.IsNullOrEmpty(valueStr) => long.Parse(valueStr),
Type t when t == typeof(ulong) && !string.IsNullOrEmpty(valueStr) => ulong.Parse(valueStr),
Type t when t == typeof(nint) && !string.IsNullOrEmpty(valueStr) => nint.Parse(valueStr),
Type t when t == typeof(nuint) && !string.IsNullOrEmpty(valueStr) => nuint.Parse(valueStr),
//Type t when t == typeof(DateTime) => string.IsNullOrEmpty(valueStr) ? 0 : DateTime.Parse(valueStr),
Type t when t == typeof(MethodDetails) => md, // 节点方法描述
Type t when t == typeof(NodeModelBase) => nodeModel, // 节点实体类
Type t when t == typeof(Guid) => new Guid(inputParameter?.ToString()),
Type t when t == typeof(DateTime) => DateTime.Parse(inputParameter?.ToString()),
Type t when t == typeof(string) => inputParameter?.ToString(),
Type t when t == typeof(char) => char.Parse(inputParameter?.ToString()),
Type t when t == typeof(bool) => inputParameter is null ? false : bool.Parse(inputParameter?.ToString()),
Type t when t == typeof(float) => inputParameter is null ? 0F : float.Parse(inputParameter?.ToString()),
Type t when t == typeof(decimal) => inputParameter is null ? 0 : decimal.Parse(inputParameter?.ToString()),
Type t when t == typeof(double) => inputParameter is null ? 0 : double.Parse(inputParameter?.ToString()),
Type t when t == typeof(sbyte) => inputParameter is null ? 0 : sbyte.Parse(inputParameter?.ToString()),
Type t when t == typeof(byte) => inputParameter is null ? 0 : byte.Parse(inputParameter?.ToString()),
Type t when t == typeof(short) => inputParameter is null ? 0 : short.Parse(inputParameter?.ToString()),
Type t when t == typeof(ushort) => inputParameter is null ? 0U : ushort.Parse(inputParameter?.ToString()),
Type t when t == typeof(int) => inputParameter is null ? 0 : int.Parse(inputParameter?.ToString()),
Type t when t == typeof(uint) => inputParameter is null ? 0U : uint.Parse(inputParameter?.ToString()),
Type t when t == typeof(long) => inputParameter is null ? 0L : long.Parse(inputParameter?.ToString()),
Type t when t == typeof(ulong) => inputParameter is null ? 0UL : ulong.Parse(inputParameter?.ToString()),
Type t when t == typeof(nint) => inputParameter is null ? 0 : nint.Parse(inputParameter?.ToString()),
Type t when t == typeof(nuint) => inputParameter is null ? 0 : nuint.Parse(inputParameter?.ToString()),
Type t when t.IsArray => (inputParameter as Array)?.Cast<object>().ToList(),
Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter,
Type t when Nullable.GetUnderlyingType(t) != null => inputParameter is null ? null : Convert.ChangeType(inputParameter, Nullable.GetUnderlyingType(t)),
_ => inputParameter,
// Type t when Nullable.GetUnderlyingType(t) != null => inputParameter is null ? null : Convert.ChangeType(inputParameter, Nullable.GetUnderlyingType(t)),
};
}
catch (Exception ex) // 节点参数类型转换异常
{
parameters[i] = null;
parameters[i] = new object();
Console.WriteLine(ex);
}
}
@@ -395,21 +399,25 @@ namespace Serein.NodeFlow.Base
}
}
private static async Task MonitorObjExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object data, int type)
private static async Task MonitorObjExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object? data, int monitorType)
{
MonitorObjectEventArgs.ObjSourceType sourceType;
string key;
if(type == 0)
string? key;
if(monitorType == 0)
{
key = data.GetType().FullName;
key = data?.GetType()?.FullName;
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
}
else
{
key = nodeModel.Guid;
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
}
if (string.IsNullOrEmpty(key))
{
return;
}
if (context.Env.CheckObjMonitorState(key, out List<string> exps)) // 如果新的数据处于查看状态通知UI进行更新交给运行环境判断
{
context.Env.MonitorObjectNotification(nodeModel.Guid, data, sourceType); // 对象处于监视状态通知UI更新数据显示

View File

@@ -46,7 +46,7 @@ namespace Serein.NodeFlow
public FlowEnvironment()
{
sereinIOC = new SereinIOC();
//ChannelFlowInterrupt = new ChannelFlowInterrupt();
ChannelFlowInterrupt = new ChannelFlowInterrupt();
//LoadedAssemblyPaths = new List<string>();
//LoadedAssemblies = new List<Assembly>();
//MethodDetailss = new List<MethodDetails>();
@@ -69,67 +69,67 @@ namespace Serein.NodeFlow
/// <summary>
/// 加载Dll
/// </summary>
public event LoadDllHandler OnDllLoad;
public event LoadDllHandler? OnDllLoad;
/// <summary>
/// 移除DLL
/// </summary>
public event RemoteDllHandler OnDllRemote;
public event RemoteDllHandler? OnDllRemote;
/// <summary>
/// 项目加载完成
/// </summary>
public event ProjectLoadedHandler OnProjectLoaded;
public event ProjectLoadedHandler? OnProjectLoaded;
/// <summary>
/// 节点连接属性改变事件
/// </summary>
public event NodeConnectChangeHandler OnNodeConnectChange;
public event NodeConnectChangeHandler? OnNodeConnectChange;
/// <summary>
/// 节点创建事件
/// </summary>
public event NodeCreateHandler OnNodeCreate;
public event NodeCreateHandler? OnNodeCreate;
/// <summary>
/// 移除节点事件
/// </summary>
public event NodeRemoteHandler OnNodeRemote;
public event NodeRemoteHandler? OnNodeRemote;
/// <summary>
/// 起始节点变化事件
/// </summary>
public event StartNodeChangeHandler OnStartNodeChange;
public event StartNodeChangeHandler? OnStartNodeChange;
/// <summary>
/// 流程运行完成事件
/// </summary>
public event FlowRunCompleteHandler OnFlowRunComplete;
public event FlowRunCompleteHandler? OnFlowRunComplete;
/// <summary>
/// 被监视的对象改变事件
/// </summary>
public event MonitorObjectChangeHandler OnMonitorObjectChange;
public event MonitorObjectChangeHandler? OnMonitorObjectChange;
/// <summary>
/// 节点中断状态改变事件
/// </summary>
public event NodeInterruptStateChangeHandler OnNodeInterruptStateChange;
public event NodeInterruptStateChangeHandler? OnNodeInterruptStateChange;
/// <summary>
/// 节点触发了中断
/// </summary>
public event ExpInterruptTriggerHandler OnInterruptTrigger;
public event ExpInterruptTriggerHandler? OnInterruptTrigger;
/// <summary>
/// 容器改变
/// </summary>
public event IOCMembersChangedHandler OnIOCMembersChanged;
public event IOCMembersChangedHandler? OnIOCMembersChanged;
/// <summary>
/// 节点需要定位
/// </summary>
public event NodeLocatedHandler OnNodeLocate;
public event NodeLocatedHandler? OnNodeLocate;
#endregion
#region
@@ -196,12 +196,12 @@ namespace Serein.NodeFlow
/// <summary>
/// 起始节点私有属性
/// </summary>
private NodeModelBase _startNode;
private NodeModelBase? _startNode = null;
/// <summary>
/// 起始节点
/// </summary>
private NodeModelBase StartNode
private NodeModelBase? StartNode
{
get
{
@@ -209,6 +209,10 @@ namespace Serein.NodeFlow
}
set
{
if (value is null)
{
return;
}
if (_startNode is not null)
{
_startNode.IsStart = false;
@@ -481,17 +485,19 @@ namespace Serein.NodeFlow
public bool RemoteDll(string assemblyFullName)
{
var library = NodeLibrarys.FirstOrDefault(nl => nl.Assembly.FullName.Equals(assemblyFullName));
var library = NodeLibrarys.FirstOrDefault(nl => assemblyFullName.Equals(nl.Assembly.FullName));
if(library is null)
{
return false;
}
var nodes = Nodes.Values.ToDictionary(
key => key.MethodDetails.MethodName,
value => value
);
if(nodes.Count == 0)
var groupedNodes = Nodes.Values
.Where(node => node.MethodDetails is not null)
.ToArray()
.GroupBy(node => node.MethodDetails!.MethodName)
.ToDictionary(
key => key.Key,
group => group.Count());
if(Nodes.Count == 0)
{
return true; // 当前无节点,可以直接删除
}
@@ -500,9 +506,12 @@ namespace Serein.NodeFlow
{
foreach(var md in mds)
{
if (nodes.ContainsKey(md.MethodName))
if(groupedNodes.TryGetValue(md.MethodName,out int count))
{
return false; // 创建过相关的节点
if (count > 0)
{
return false; // 创建过相关的节点
}
}
}
MethodDetailss.Remove(library);
@@ -510,7 +519,7 @@ namespace Serein.NodeFlow
}
else
{
return true;
return true;
}
}
@@ -685,9 +694,10 @@ namespace Serein.NodeFlow
}
}
public bool TryGetDelegate(string methodName, out Delegate del)
public bool TryGetDelegate(string methodName, out Delegate? del)
{
if (MethodDelegates.TryGetValue(methodName, out del))
if (!string.IsNullOrEmpty(methodName) && MethodDelegates.TryGetValue(methodName, out del))
{
return del != null;
}
@@ -742,7 +752,7 @@ namespace Serein.NodeFlow
return false;
}
nodeModel.DebugSetting.InterruptClass = interruptClass;
OnNodeInterruptStateChange.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, interruptClass));
OnNodeInterruptStateChange?.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, interruptClass));
return true;
}
@@ -958,11 +968,19 @@ namespace Serein.NodeFlow
{
// 加载DLL创建 MethodDetails、实例作用对象、委托方法
var assemblyName = type.Assembly.GetName().Name;
if (string.IsNullOrEmpty(assemblyName))
{
continue;
}
var methods = MethodDetailsHelperTmp.GetMethodsToProcess(type);
foreach(var method in methods)
{
(var md, var del) = MethodDetailsHelperTmp.CreateMethodDetails(type, method, assemblyName);
if(md is null || del is null)
{
Console.WriteLine($"无法加载方法信息:{assemblyName}-{type}-{method}");
continue;
}
if (MethodDelegates.TryAdd(md.MethodName, del))
{
methodDetails.Add(md);

View File

@@ -30,7 +30,7 @@ namespace Serein.NodeFlow.Model
{
return [];
}
internal override NodeInfo ToInfo()
internal override NodeInfo? ToInfo()
{
if (MethodDetails is null) return null;

View File

@@ -52,7 +52,7 @@ public static class MethodDetailsHelperTmp
/// 创建方法信息
/// </summary>
/// <returns></returns>
public static (MethodDetails,Delegate) CreateMethodDetails(Type type, MethodInfo method, string assemblyName)
public static (MethodDetails?,Delegate?) CreateMethodDetails(Type type, MethodInfo method, string assemblyName)
{
var methodName = method.Name;

View File

@@ -1,13 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection;
using System.Reflection.Emit;
@@ -78,7 +69,7 @@ namespace Serein.NodeFlow.Tool
// 如果类型已经缓存,直接返回缓存的类型
if (typeCache.ContainsKey(typeName))
{
return Activator.CreateInstance(typeCache[typeName]);
return Activator.CreateInstance(typeCache[typeName])!;
}
// 定义动态程序集和模块
@@ -98,7 +89,7 @@ namespace Serein.NodeFlow.Tool
if (propValue is IList<Dictionary<string, object>>) // 处理数组类型
{
var nestedPropValue = (propValue as IList<Dictionary<string, object>>)[0];
var nestedPropValue = (propValue as IList<Dictionary<string, object>>)![0];
var nestedType = CreateObjectWithProperties(nestedPropValue, $"{propName}Element");
propType = nestedType.GetType().MakeArrayType(); // 创建数组类型
}
@@ -152,7 +143,7 @@ namespace Serein.NodeFlow.Tool
typeCache[typeName] = dynamicType;
// 创建对象实例
return Activator.CreateInstance(dynamicType);
return Activator.CreateInstance(dynamicType)!;
}
// 方法 2: 递归设置对象的属性值
@@ -169,10 +160,10 @@ namespace Serein.NodeFlow.Tool
if (value is Dictionary<string, object> nestedProperties)
{
// 创建嵌套对象
var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
var nestedObj = Activator.CreateInstance(propInfo!.PropertyType);
// 递归设置嵌套对象的值
SetPropertyValues(nestedObj, nestedProperties);
SetPropertyValues(nestedObj!, nestedProperties);
// 将嵌套对象赋值给属性
propInfo.SetValue(obj, nestedObj);
@@ -180,7 +171,7 @@ namespace Serein.NodeFlow.Tool
else
{
// 直接赋值给属性
propInfo.SetValue(obj, value);
propInfo!.SetValue(obj, value);
}
}
}
@@ -222,7 +213,7 @@ namespace Serein.NodeFlow.Tool
if (propValue is Dictionary<string, object> nestedProperties)
{
var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
if (SetPropertyValuesWithValidation(nestedObj, nestedProperties))
if (nestedObj is not null && SetPropertyValuesWithValidation(nestedObj, nestedProperties))
{
propInfo.SetValue(obj, nestedObj);
}
@@ -235,22 +226,24 @@ namespace Serein.NodeFlow.Tool
{
// 获取目标类型的数组元素类型
var elementType = propInfo.PropertyType.GetElementType();
var array = Array.CreateInstance(elementType, list.Count);
for (int i = 0; i < list.Count; i++)
if (elementType is not null)
{
var item = Activator.CreateInstance(elementType);
if (SetPropertyValuesWithValidation(item, list[i]))
{
array.SetValue(item, i);
}
else
{
allSuccessful = false; // 赋值失败
}
}
var array = Array.CreateInstance(elementType, list.Count);
propInfo.SetValue(obj, array);
for (int i = 0; i < list.Count; i++)
{
var item = Activator.CreateInstance(elementType);
if (item is not null && SetPropertyValuesWithValidation(item, list[i]))
{
array.SetValue(item, i);
}
else
{
allSuccessful = false; // 赋值失败
}
}
propInfo.SetValue(obj, array);
}
}
else
{

View File

@@ -39,7 +39,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
return false;
}
private object GetMemberValue(object? obj, string memberPath)
private object? GetMemberValue(object? obj, string memberPath)
{
string[] members = memberPath[1..].Split('.');
foreach (var member in members)

View File

@@ -29,7 +29,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
}
}
public static SereinConditionResolver ConditionParse(object data, string expression)
public static SereinConditionResolver ConditionParse(object? data, string expression)
{
if (expression.StartsWith('.') || expression.StartsWith('<')) // 表达式前缀属于从上一个节点数据对象获取成员值
{
@@ -91,7 +91,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <summary>
/// 解析对象表达式
/// </summary>
private static SereinConditionResolver ParseObjectExpression(object data, string expression)
private static SereinConditionResolver ParseObjectExpression(object? data, string expression)
{
var parts = expression.Split(' ');
string operatorStr = parts[0]; // 获取操作类型

View File

@@ -41,7 +41,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="NotSupportedException"></exception>
public static object Evaluate(string expression, object targetObJ, out bool isChange)
public static object? Evaluate(string expression, object targetObJ, out bool isChange)
{
var parts = expression.Split([' '], 2);
if (parts.Length != 2)
@@ -84,7 +84,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <param name="methodCall">方法名称</param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
private static object InvokeMethod(object target, string methodCall)
private static object? InvokeMethod(object target, string methodCall)
{
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
if (methodParts.Length != 2)
@@ -98,12 +98,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
.Select(p => p.Trim())
.ToArray();
var method = target.GetType().GetMethod(methodName);
if (method is null)
{
throw new ArgumentException($"Method {methodName} not found on target.");
}
var method = target.GetType().GetMethod(methodName) ?? throw new ArgumentException($"Method {methodName} not found on target.");
var parameterValues = method.GetParameters()
.Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType))
.ToArray();
@@ -119,15 +114,14 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <param name="memberPath">属性路径</param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
private static object GetMember(object target, string memberPath)
private static object? GetMember(object? target, string memberPath)
{
if (target is null) return null;
// 分割成员路径,按 '.' 处理多级访问
var members = memberPath.Split('.');
foreach (var member in members)
{
if (target == null) return null;
// 检查成员是否包含数组索引,例如 "cars[0]"
var arrayIndexStart = member.IndexOf('[');
if (arrayIndexStart != -1)
@@ -148,22 +142,22 @@ namespace Serein.NodeFlow.Tool.SereinExpression
}
// 获取数组或集合对象
var arrayProperty = target.GetType().GetProperty(arrayName);
if (arrayProperty != null)
var arrayProperty = target?.GetType().GetProperty(arrayName);
if (arrayProperty is null)
{
target = arrayProperty.GetValue(target);
}
else
{
var arrayField = target.GetType().GetField(arrayName);
if (arrayField != null)
{
target = arrayField.GetValue(target);
}
else
var arrayField = target?.GetType().GetField(arrayName);
if (arrayField is null)
{
throw new ArgumentException($"Member {arrayName} not found on target.");
}
else
{
target = arrayField.GetValue(target);
}
}
else
{
target = arrayProperty.GetValue(target);
}
// 访问数组或集合中的指定索引
@@ -191,22 +185,22 @@ namespace Serein.NodeFlow.Tool.SereinExpression
else
{
// 处理非数组情况的属性或字段
var property = target.GetType().GetProperty(member);
if (property != null)
var property = target?.GetType().GetProperty(member);
if (property is null)
{
target = property.GetValue(target);
}
else
{
var field = target.GetType().GetField(member);
if (field != null)
{
target = field.GetValue(target);
}
else
var field = target?.GetType().GetField(member);
if (field is null)
{
throw new ArgumentException($"Member {member} not found on target.");
}
else
{
target = field.GetValue(target);
}
}
else
{
target = property.GetValue(target);
}
}
}
@@ -220,7 +214,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <param name="assignment">属性路径 </param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
private static object SetMember(object target, string assignment)
private static object? SetMember(object? target, string assignment)
{
var parts = assignment.Split(new[] { '=' }, 2);
if (parts.Length != 2)
@@ -238,7 +232,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
// 检查是否包含数组索引
var arrayIndexStart = member.IndexOf('[');
if (arrayIndexStart != -1)
if (arrayIndexStart != -1)
{
// 解析数组名和索引
var arrayName = member.Substring(0, arrayIndexStart);
@@ -255,24 +249,24 @@ namespace Serein.NodeFlow.Tool.SereinExpression
}
// 获取数组或集合
var arrayProperty = target.GetType().GetProperty(arrayName);
if (arrayProperty != null)
var arrayProperty = target?.GetType().GetProperty(arrayName);
if (arrayProperty is null)
{
target = arrayProperty.GetValue(target);
}
else
{
var arrayField = target.GetType().GetField(arrayName);
if (arrayField != null)
{
target = arrayField.GetValue(target);
}
else
var arrayField = target?.GetType().GetField(arrayName);
if (arrayField is null)
{
throw new ArgumentException($"Member {arrayName} not found on target.");
}
else
{
target = arrayField.GetValue(target);
}
}
else
{
target = arrayProperty.GetValue(target);
}
// 获取目标数组或集合中的指定元素
if (target is Array array)
@@ -299,46 +293,47 @@ namespace Serein.NodeFlow.Tool.SereinExpression
else
{
// 处理非数组情况的属性或字段
var property = target.GetType().GetProperty(member);
if (property != null)
var property = target?.GetType().GetProperty(member);
if (property is null)
{
target = property.GetValue(target);
}
else
{
var field = target.GetType().GetField(member);
if (field != null)
{
target = field.GetValue(target);
}
else
var field = target?.GetType().GetField(member);
if (field is null)
{
throw new ArgumentException($"Member {member} not found on target.");
}
else
{
target = field.GetValue(target);
}
}
else
{
target = property.GetValue(target);
}
}
}
// 设置值
var lastMember = members.Last();
var lastProperty = target.GetType().GetProperty(lastMember);
if (lastProperty != null)
var lastProperty = target?.GetType().GetProperty(lastMember);
if (lastProperty is null)
{
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
lastProperty.SetValue(target, convertedValue);
}
else
{
var lastField = target.GetType().GetField(lastMember);
if (lastField != null)
var lastField = target?.GetType().GetField(lastMember);
if (lastField is null)
{
throw new ArgumentException($"Member {lastMember} not found on target.");
}
else
{
var convertedValue = Convert.ChangeType(value, lastField.FieldType);
lastField.SetValue(target, convertedValue);
}
else
{
throw new ArgumentException($"Member {lastMember} not found on target.");
}
}
else
{
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
lastProperty.SetValue(target, convertedValue);
}
return target;

View File

@@ -18,7 +18,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Library.Framework",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\Serein.Library.csproj", "{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net461DllTest", "Net461DllTest\Net461DllTest.csproj", "{E40EE629-1A38-4011-88E3-9AD036869987}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net462DllTest", "Net462DllTest\Net462DllTest.csproj", "{E40EE629-1A38-4011-88E3-9AD036869987}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@@ -12,24 +12,24 @@ namespace Serein.WorkBench
/// </summary>
public partial class App : Application
{
public class TestObject
{
//public class TestObject
//{
public NestedObject Data { get; set; }
// public NestedObject Data { get; set; }
public class NestedObject
{
public int Code { get; set; }
public int Code2 { get; set; }
// public class NestedObject
// {
// public int Code { get; set; }
// public int Code2 { get; set; }
public string Tips { get; set; }
// public string Tips { get; set; }
}
public string ToUpper(string input)
{
return input.ToUpper();
}
}
// }
// public string ToUpper(string input)
// {
// return input.ToUpper();
// }
//}
@@ -125,7 +125,7 @@ namespace Serein.WorkBench
/// 成功加载的工程文件
/// </summary>
public static SereinProjectData? FlowProjectData { get; set; }
public static string FileDataPath = "";
public static string FileDataPath { get; set; } = "";
private void Application_Startup(object sender, StartupEventArgs e)
{
// 检查是否传入了参数
@@ -163,7 +163,7 @@ namespace Serein.WorkBench
string filePath;
//filePath = @"F:\临时\project\tmp\project.dnf";
//filePath = @"D:\Project\C#\TestNetFramework\Net45DllTest\Net45DllTest\bin\Debug\project.dnf";
filePath = @"D:\Project\C#\DynamicControl\SereinFlow\Net461DllTest\bin\Debug\project.dnf";
filePath = @"D:\Project\C#\DynamicControl\SereinFlow\Net462DllTest\bin\Debug\project.dnf";
//string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf";
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content);

View File

@@ -139,15 +139,15 @@ namespace Serein.WorkBench
/// <summary>
/// 组合变换容器
/// </summary>
private TransformGroup canvasTransformGroup;
private readonly TransformGroup canvasTransformGroup;
/// <summary>
/// 缩放画布
/// </summary>
private ScaleTransform scaleTransform;
private readonly ScaleTransform scaleTransform;
/// <summary>
/// 平移画布
/// </summary>
private TranslateTransform translateTransform;
private readonly TranslateTransform translateTransform;
#endregion
public MainWindow()
@@ -162,10 +162,17 @@ namespace Serein.WorkBench
InitFlowEnvironmentEvent(); // 配置环境事件
logWindow = InitConsoleOut(); // 重定向 Console 输出
InitCanvasUI(); // 配置画布
canvasTransformGroup = new TransformGroup();
scaleTransform = new ScaleTransform();
translateTransform = new TranslateTransform();
canvasTransformGroup.Children.Add(scaleTransform);
canvasTransformGroup.Children.Add(translateTransform);
FlowChartCanvas.RenderTransform = canvasTransformGroup;
if(App.FlowProjectData is not null)
if (App.FlowProjectData is not null)
{
FlowEnvironment.LoadProject(App.FlowProjectData, App.FileDataPath); // 加载项目
}
@@ -198,17 +205,7 @@ namespace Serein.WorkBench
private void InitCanvasUI()
{
canvasTransformGroup = new TransformGroup();
scaleTransform = new ScaleTransform();
translateTransform = new TranslateTransform();
canvasTransformGroup.Children.Add(scaleTransform);
canvasTransformGroup.Children.Add(translateTransform);
FlowChartCanvas.RenderTransform = canvasTransformGroup;
}
private LogWindow InitConsoleOut()
{
@@ -243,7 +240,7 @@ namespace Serein.WorkBench
}
var canvasData = project.Basic.Canvas;
if (canvasData != null)
if (canvasData is not null)
{
scaleTransform.ScaleX = 1;
scaleTransform.ScaleY = 1;
@@ -355,9 +352,9 @@ namespace Serein.WorkBench
End = toNode,
Type = connectionType
};
if (toNode is FlipflopNodeControl flipflopControl) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
if (toNode is FlipflopNodeControl flipflopControl
&& flipflopControl?.ViewModel?.Node is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
{
var nodeModel = flipflopControl?.ViewModel?.Node;
NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
}
@@ -556,7 +553,7 @@ namespace Serein.WorkBench
}
else
{
if (ViewObjectViewer.MonitorKey.Equals(monitorKey)) // 相同对象
if (monitorKey.Equals(ViewObjectViewer.MonitorKey)) // 相同对象
{
ViewObjectViewer.RefreshObjectTree(eventArgs.NewData); // 刷新
}
@@ -704,7 +701,7 @@ namespace Serein.WorkBench
/// <param name="power">抖动第一下偏移量</param>
/// <param name="range">减弱幅度小于等于power大于0</param>
/// <param name="speed">持续系数(大于0),越大时间越长,</param>
private static void ElasticAnimation(NodeControlBase? nodeControl, TranslateTransform translate, int power, int range = 1, double speed = 1)
private static void ElasticAnimation(NodeControlBase nodeControl, TranslateTransform translate, int power, int range = 1, double speed = 1)
{
DoubleAnimationUsingKeyFrames animation1 = new DoubleAnimationUsingKeyFrames();
for (int i = power, j = 1; i >= 0; i -= range)
@@ -883,10 +880,10 @@ namespace Serein.WorkBench
/// <param name="nodeControl"><para> 任何情景下都尽量避免直接操作 ViewModel 中的 NodeModel 节点,而是应该调用 FlowEnvironment 提供接口进行操作。</para> 因为 Workbench 应该更加关注UI视觉效果而非直接干扰流程环境运行的逻辑。<para> 之所以暴露 NodeModel 属性,因为有些场景下不可避免的需要直接获取节点的属性。</para> </param>
private void ConfigureContextMenu(NodeControlBase nodeControl)
{
var contextMenu = new ContextMenu();
// var nodeModel = nodeControl.ViewModel.Node;
if (nodeControl.ViewModel.Node?.MethodDetails?.ReturnType is Type returnType && returnType != typeof(void))
{
contextMenu.Items.Add(CreateMenuItem("查看返回类型", (s, e) =>
@@ -894,7 +891,7 @@ namespace Serein.WorkBench
DisplayReturnTypeTreeViewer(returnType);
}));
}
var nodeGuid = nodeControl?.ViewModel?.Node?.Guid;
var nodeGuid = nodeControl.ViewModel?.Node?.Guid;
#region -
@@ -923,10 +920,10 @@ namespace Serein.WorkBench
contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => FlowEnvironment.SetStartNode(nodeGuid)));
contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => FlowEnvironment.RemoteNode(nodeGuid)));
contextMenu.Items.Add(CreateMenuItem("添加 真分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsSucceed)));
contextMenu.Items.Add(CreateMenuItem("添加 假分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsFail)));
contextMenu.Items.Add(CreateMenuItem("添加 异常分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsError)));
contextMenu.Items.Add(CreateMenuItem("添加 上游分支", (s, e) => StartConnection(nodeControl, ConnectionType.Upstream)));
contextMenu.Items.Add(CreateMenuItem("添加 真分支", (s, e) => StartConnection((NodeControlBase)s, ConnectionType.IsSucceed)));
contextMenu.Items.Add(CreateMenuItem("添加 假分支", (s, e) => StartConnection((NodeControlBase)s, ConnectionType.IsFail)));
contextMenu.Items.Add(CreateMenuItem("添加 异常分支", (s, e) => StartConnection((NodeControlBase)s, ConnectionType.IsError)));
contextMenu.Items.Add(CreateMenuItem("添加 上游分支", (s, e) => StartConnection((NodeControlBase)s, ConnectionType.Upstream)));
@@ -976,6 +973,10 @@ namespace Serein.WorkBench
{
var contextMenu = new ContextMenu();
contextMenu.Items.Add(CreateMenuItem("删除连线", (s, e) => DeleteConnection(connection)));
if (connection.ArrowPath is null || connection.BezierPath is null)
{
return;
}
connection.ArrowPath.ContextMenu = contextMenu;
connection.BezierPath.ContextMenu = contextMenu;
}
@@ -1198,8 +1199,8 @@ namespace Serein.WorkBench
// 准备放置条件表达式控件
if (nodeControl.ViewModel.Node.ControlType == NodeControlType.ExpCondition)
{
ConditionRegionControl conditionRegion = GetParentOfType<ConditionRegionControl>(hitElement);
if (conditionRegion != null)
ConditionRegionControl? conditionRegion = GetParentOfType<ConditionRegionControl>(hitElement);
if (conditionRegion is not null)
{
TryPlaceNodeInRegion(conditionRegion, nodeControl);
//// 如果存在条件区域容器
@@ -1222,8 +1223,8 @@ namespace Serein.WorkBench
// 准备放置条件表达式控件
if (nodeControl.ViewModel.Node.ControlType == NodeControlType.ExpCondition)
{
ConditionRegionControl conditionRegion = regionControl as ConditionRegionControl;
if (conditionRegion != null)
ConditionRegionControl? conditionRegion = regionControl as ConditionRegionControl;
if (conditionRegion is not null)
{
// 如果存在条件区域容器
conditionRegion.AddCondition(nodeControl);
@@ -1365,14 +1366,17 @@ namespace Serein.WorkBench
}
private void ChangeViewerObjOfNode(NodeControlBase nodeControl)
{
var node = nodeControl?.ViewModel?.Node;
var node = nodeControl.ViewModel.Node;
//if (node is not null && (node.MethodDetails is null || node.MethodDetails.ReturnType != typeof(void))
if (node is not null && node?.MethodDetails?.ReturnType != typeof(void))
if (node is not null && node.MethodDetails?.ReturnType != typeof(void))
{
var key = node.Guid;
var instance = node.GetFlowData();
ViewObjectViewer.LoadObjectInformation(key, instance);
ChangeViewerObj(key, instance);
if(instance is not null)
{
ViewObjectViewer.LoadObjectInformation(key, instance);
ChangeViewerObj(key, instance);
}
}
}
public void ChangeViewerObj(string key, object instance)
@@ -1768,64 +1772,6 @@ namespace Serein.WorkBench
}
e.Handled = true; // 防止事件传播影响其他控件
return;
// 如果正在选取状态,再次点击画布时自动确定选取范围,否则进入选取状态
if (IsSelectControl)
{
IsSelectControl = false;
// 释放鼠标捕获
FlowChartCanvas.ReleaseMouseCapture();
// 隐藏选取矩形(如果需要保持选取状态显示,可以删除此行)
SelectionRectangle.Visibility = Visibility.Collapsed;
// 处理选取区域内的元素(例如,获取选取范围内的控件)
Rect selectionArea = new Rect(Canvas.GetLeft(SelectionRectangle),
Canvas.GetTop(SelectionRectangle),
SelectionRectangle.Width,
SelectionRectangle.Height);
// 在此处处理选取的逻辑
foreach (UIElement element in FlowChartCanvas.Children)
{
Rect elementBounds = new Rect(Canvas.GetLeft(element), Canvas.GetTop(element),
element.RenderSize.Width, element.RenderSize.Height);
if (selectionArea.Contains(elementBounds))
{
// 选中元素,执行相应操作
if (element is NodeControlBase control)
{
selectNodeControls.Add(control);
}
}
}
SelectedNode();// 选择之后需要执行的操作
}
else
{
// 进入选取状态
IsSelectControl = true;
// 开始选取时,记录鼠标起始点
startSelectControolPoint = e.GetPosition(FlowChartCanvas);
// 初始化选取矩形的位置和大小
Canvas.SetLeft(SelectionRectangle, startSelectControolPoint.X);
Canvas.SetTop(SelectionRectangle, startSelectControolPoint.Y);
SelectionRectangle.Width = 0;
SelectionRectangle.Height = 0;
// 显示选取矩形
SelectionRectangle.Visibility = Visibility.Visible;
SelectionRectangle.ContextMenu ??= ConfiguerSelectionRectangle();
// 捕获鼠标以便在鼠标移动到Canvas外部时仍能处理事件
FlowChartCanvas.CaptureMouse();
}
e.Handled = true; // 防止事件传播影响其他控件
}
/// <summary>
@@ -2323,13 +2269,13 @@ namespace Serein.WorkBench
/// <typeparam name="T"></typeparam>
/// <param name="element"></param>
/// <returns></returns>
private static T GetParentOfType<T>(DependencyObject element) where T : DependencyObject
private static T? GetParentOfType<T>(DependencyObject element) where T : DependencyObject
{
while (element != null)
{
if (element is T)
if (element is T e)
{
return element as T;
return e;
}
element = VisualTreeHelper.GetParent(element);
}
@@ -2342,9 +2288,9 @@ namespace Serein.WorkBench
private void JudgmentFlipFlopNode(NodeControlBase nodeControl)
{
if (nodeControl is FlipflopNodeControl flipflopControl) // 判断是否为触发器
if (nodeControl is FlipflopNodeControl flipflopControl
&& flipflopControl?.ViewModel?.Node is NodeModelBase nodeModel) // 判断是否为触发器
{
var nodeModel = flipflopControl?.ViewModel?.Node;
int count = 0;
foreach (var ct in NodeStaticConfig.ConnectionTypes)
{
@@ -2473,13 +2419,18 @@ namespace Serein.WorkBench
node.Position = new Position(positionRelativeToParent.X, positionRelativeToParent.Y);
}
}
var isPass = SaveContentToFile(out string savePath, out Action<string,string>? savaProjectFile);
if(!isPass)
if (!SaveContentToFile(out string savePath, out Action<string, string>? savaProjectFile))
{
Console.WriteLine("保存项目DLL时返回了意外的文件保存路径");
return;
}
string librarySavePath = System.IO.Path.GetDirectoryName(savePath);
string? librarySavePath = System.IO.Path.GetDirectoryName(savePath);
if (string.IsNullOrEmpty(librarySavePath))
{
Console.WriteLine("保存项目DLL时返回了意外的文件保存路径");
return;
}
Console.WriteLine(savePath);
for (int index = 0; index < projectData.Librarys.Length; index++)
{
@@ -2578,17 +2529,17 @@ namespace Serein.WorkBench
/// <param name="e"></param>
private void ButtonTestExpObj_Click(object sender, RoutedEventArgs e)
{
string jsonString =
"""
{
"Name": "张三",
"Age": 24,
"Address": {
"City": "北京",
"PostalCode": "10000"
}
}
""";
//string jsonString =
//"""
//{
// "Name": "张三",
// "Age": 24,
// "Address": {
// "City": "北京",
// "PostalCode": "10000"
// }
//}
//""";
var externalData = new Dictionary<string, object>
{
@@ -2615,12 +2566,12 @@ namespace Serein.WorkBench
Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
}
ObjDynamicCreateHelper.PrintObjectProperties(result);
ObjDynamicCreateHelper.PrintObjectProperties(result!);
Console.WriteLine( );
var exp = "@set .Addresses[1].Street = qwq";
var data = SerinExpressionEvaluator.Evaluate(exp, result, out bool isChange);
var data = SerinExpressionEvaluator.Evaluate(exp, result!, out bool isChange);
exp = "@get .Addresses[1].Street";
data = SerinExpressionEvaluator.Evaluate(exp,result, out isChange);
data = SerinExpressionEvaluator.Evaluate(exp,result!, out isChange);
Console.WriteLine($"{exp} => {data}");
}
@@ -2681,22 +2632,29 @@ namespace Serein.WorkBench
canvas.Dispatcher.InvokeAsync(() =>
{
if (connection is not null && connection.BezierPath is null)
if (connection is null)
{
return;
}
if (connection.BezierPath is null)
{
connection.BezierPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
//Canvas.SetZIndex(connection.BezierPath, -1);
canvas.Children.Add(connection.BezierPath);
}
if (connection is not null && connection.ArrowPath is null)
if (connection.ArrowPath is null)
{
connection.ArrowPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), Fill = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
//Canvas.SetZIndex(connection.ArrowPath, -1);
canvas.Children.Add(connection.ArrowPath);
}
BezierLineDrawer.UpdateBezierLine(canvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath);
isUpdating = false;
});
}
@@ -2746,10 +2704,10 @@ namespace Serein.WorkBench
public class Connection
{
public ConnectionType Type { get; set; }
public Canvas Canvas { get; set; }// 贝塞尔曲线所在画布
public Canvas? Canvas { get; set; }// 贝塞尔曲线所在画布
public System.Windows.Shapes.Path BezierPath { get; set; }// 贝塞尔曲线路径
public System.Windows.Shapes.Path ArrowPath { get; set; } // 箭头路径
public System.Windows.Shapes.Path? BezierPath { get; set; }// 贝塞尔曲线路径
public System.Windows.Shapes.Path? ArrowPath { get; set; } // 箭头路径
public required NodeControlBase Start { get; set; } // 起始
public required NodeControlBase End { get; set; } // 结束
@@ -2757,8 +2715,11 @@ namespace Serein.WorkBench
public void RemoveFromCanvas()
{
Canvas.Children.Remove(BezierPath); // 移除线
Canvas.Children.Remove(ArrowPath); // 移除线
if(Canvas != null)
{
Canvas.Children.Remove(BezierPath); // 移除线
Canvas.Children.Remove(ArrowPath); // 移除线
}
}
/// <summary>
@@ -2766,6 +2727,10 @@ namespace Serein.WorkBench
/// </summary>
public void Refresh()
{
if(Canvas is null || BezierPath is null || ArrowPath is null)
{
return;
}
BezierLineDrawer.UpdateBezierLine(Canvas, Start, End, BezierPath, ArrowPath);
}
}

View File

@@ -12,7 +12,6 @@ namespace Serein.WorkBench.Node.View
/// </summary>
public partial class ConditionRegionControl : NodeControlBase
{
private Point _dragStartPoint;
public ConditionRegionControl() : base()
{

View File

@@ -74,7 +74,7 @@ namespace Serein.WorkBench.Themes
{ConnectionType.IsError, []},
}
};
string itemName = rootNodeModel?.MethodDetails?.MethodTips;
string? itemName = rootNodeModel.MethodDetails?.MethodTips;
if (string.IsNullOrEmpty(itemName))
{
itemName = rootNodeModel.ControlType.ToString();
@@ -141,10 +141,10 @@ namespace Serein.WorkBench.Themes
RootNode = child,
ChildNodes = child.SuccessorNodes,
};
string itemName = child?.MethodDetails?.MethodTips;
string? itemName = child?.MethodDetails?.MethodTips;
if (string.IsNullOrEmpty(itemName))
{
itemName = child.ControlType.ToString();
itemName = child?.ControlType.ToString();
}
TreeViewItem treeViewItem = new TreeViewItem
{
@@ -204,10 +204,10 @@ namespace Serein.WorkBench.Themes
ChildNodes = childNodeModel.SuccessorNodes,
};
string itemName = childNodeModel?.MethodDetails?.MethodTips;
string? itemName = childNodeModel?.MethodDetails?.MethodTips;
if (string.IsNullOrEmpty(itemName))
{
itemName = childNodeModel.ControlType.ToString();
itemName = childNodeModel?.ControlType.ToString();
}
TreeViewItem treeViewItem = new TreeViewItem
{

View File

@@ -22,7 +22,6 @@ namespace Serein.WorkBench.Themes
/// </summary>
public partial class NodeTreeViewControl : UserControl
{
private IFlowEnvironment FlowEnvironment { get; set; }
public NodeTreeViewControl()
{
InitializeComponent();

View File

@@ -32,7 +32,7 @@ namespace Serein.WorkBench.Themes
/// <summary>
/// 属性名称
/// </summary>
public string Name { get; set; }
public string? Name { get; set; }
/// <summary>
/// 属性类型
/// </summary>
@@ -40,15 +40,15 @@ namespace Serein.WorkBench.Themes
/// <summary>
/// 数据类型
/// </summary>
public Type DataType { get; set; }
public Type? DataType { get; set; }
/// <summary>
/// 数据
/// </summary>
public object DataValue { get; set; }
public object? DataValue { get; set; }
/// <summary>
/// 数据路径
/// </summary>
public string DataPath { get; set; }
public string DataPath { get; set; } = string.Empty;
}
@@ -80,24 +80,24 @@ namespace Serein.WorkBench.Themes
/// <summary>
/// 运行环境
/// </summary>
public IFlowEnvironment FlowEnvironment { get; set; }
public IFlowEnvironment? FlowEnvironment { get; set; }
/// <summary>
/// 监视对象的键
/// </summary>
public string MonitorKey { get => monitorKey; }
public string? MonitorKey { get => monitorKey; }
/// <summary>
/// 正在监视的对象
/// </summary>
public object MonitorObj { get => monitorObj; }
public object? MonitorObj { get => monitorObj; }
/// <summary>
/// 监视表达式
/// </summary>
public string MonitorExpression { get => ExpressionTextBox.Text.ToString(); }
public string? MonitorExpression { get => ExpressionTextBox.Text.ToString(); }
private string monitorKey;
private object monitorObj;
private string? monitorKey;
private object? monitorObj;
// 用于存储当前展开的节点路径
private HashSet<string> expandedNodePaths = new HashSet<string>();
@@ -133,7 +133,7 @@ namespace Serein.WorkBench.Themes
/// <param name="e"></param>
private void UpMonitorExpressionButton_Click(object sender, RoutedEventArgs e)
{
if (FlowEnvironment.AddInterruptExpression(monitorKey, MonitorExpression)) // 对象预览器尝试添加中断表达式
if (FlowEnvironment is not null && FlowEnvironment.AddInterruptExpression(monitorKey, MonitorExpression)) // 对象预览器尝试添加中断表达式
{
if (string.IsNullOrEmpty(MonitorExpression))
{
@@ -146,7 +146,7 @@ namespace Serein.WorkBench.Themes
}
}
private TreeViewItem? LoadTree(object obj)
private TreeViewItem? LoadTree(object? obj)
{
if (obj is null) return null;
var objectType = obj.GetType();
@@ -178,7 +178,7 @@ namespace Serein.WorkBench.Themes
/// <summary>
/// 刷新对象属性树
/// </summary>
public void RefreshObjectTree(object obj)
public void RefreshObjectTree(object? obj)
{
monitorObj = obj;
var rootNode = LoadTree(obj);
@@ -222,14 +222,20 @@ namespace Serein.WorkBench.Themes
{
if (item.Tag is FlowDataDetails flowDataDetails) // FlowDataDetails flowDataDetails object obj
{
if (flowDataDetails.ItemType == TreeItemType.Item || item.Items.Count == 0)
if (flowDataDetails.ItemType != TreeItemType.Item && item.Items.Count != 0)
{
// 记录当前节点的路径
var path = flowDataDetails.DataPath;
expandedNodePaths.Add(path);
AddMembersToTreeNode(item, flowDataDetails.DataValue, flowDataDetails.DataType);
return;
}
if(flowDataDetails.DataValue is null || flowDataDetails.DataType is null)
{
return;
}
// 记录当前节点的路径
var path = flowDataDetails.DataPath;
expandedNodePaths.Add(path);
AddMembersToTreeNode(item, flowDataDetails.DataValue, flowDataDetails.DataType);
}
}
}
@@ -276,8 +282,8 @@ namespace Serein.WorkBench.Themes
continue;
}
TreeViewItem memberNode = ConfigureTreeViewItem(obj, member); // 根据对象成员生成节点对象
if (memberNode != null)
TreeViewItem? memberNode = ConfigureTreeViewItem(obj, member); // 根据对象成员生成节点对象
if (memberNode is not null)
{
treeViewNode.Items.Add(memberNode); // 添加到当前节点
@@ -305,7 +311,7 @@ namespace Serein.WorkBench.Themes
/// <param name="obj"></param>
/// <param name="member"></param>
/// <returns></returns>
private TreeViewItem ConfigureTreeViewItem(object obj, MemberInfo member)
private TreeViewItem? ConfigureTreeViewItem(object obj, MemberInfo member)
{
if (obj == null)
{
@@ -353,7 +359,7 @@ namespace Serein.WorkBench.Themes
else
{
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
string propertyValue = GetPropertyValue(obj, property, out object value);
string propertyValue = GetPropertyValue(obj, property, out object? value);
memberNode.Tag = new FlowDataDetails
{
ItemType = TreeItemType.Property,
@@ -417,7 +423,7 @@ namespace Serein.WorkBench.Themes
else
{
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
string fieldValue = GetFieldValue(obj, field, out object value);
string fieldValue = GetFieldValue(obj, field, out object? value);
memberNode.Tag = new FlowDataDetails
{
@@ -454,7 +460,7 @@ namespace Serein.WorkBench.Themes
/// <param name="obj"></param>
/// <param name="property"></param>
/// <returns></returns>
private string GetPropertyValue(object obj, PropertyInfo property,out object value)
private string GetPropertyValue(object obj, PropertyInfo property,out object? value)
{
try
{
@@ -482,7 +488,7 @@ namespace Serein.WorkBench.Themes
/// <param name="obj"></param>
/// <param name="field"></param>
/// <returns></returns>
private string GetFieldValue(object obj, FieldInfo field, out object value)
private string GetFieldValue(object obj, FieldInfo field, out object? value)
{
try
{

View File

@@ -227,10 +227,10 @@ namespace Serein.WorkBench.Themes
/// </summary>
/// <param name="node">目标节点</param>
/// <returns>父节点</returns>
private TreeViewItem GetParentTreeViewItem(TreeViewItem node)
private TreeViewItem? GetParentTreeViewItem(TreeViewItem node)
{
DependencyObject parent = VisualTreeHelper.GetParent(node);
while (parent != null && !(parent is TreeViewItem))
while (parent != null && parent is not TreeViewItem)
{
parent = VisualTreeHelper.GetParent(parent);
}

View File

@@ -29,7 +29,7 @@ namespace Serein.WorkBench.Tool.Converters
return boolValue ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType,
public object? ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return null;