diff --git a/SpineViewer/Dialogs/BatchOpenSpineDialog.cs b/SpineViewer/Dialogs/BatchOpenSpineDialog.cs index 70bf944..5e753eb 100644 --- a/SpineViewer/Dialogs/BatchOpenSpineDialog.cs +++ b/SpineViewer/Dialogs/BatchOpenSpineDialog.cs @@ -48,7 +48,7 @@ namespace SpineViewer.Dialogs } } - if (version != Spine.Version.Auto && !Spine.Spine.ImplementedVersions.Contains(version)) + if (version != Spine.Version.Auto && !Spine.Spine.HasImplementation(version)) { MessageBox.Info($"{version.GetName()} 版本尚未实现(咕咕咕~)"); return; diff --git a/SpineViewer/Dialogs/ConvertFileFormatDialog.cs b/SpineViewer/Dialogs/ConvertFileFormatDialog.cs index 67ec272..a3363cb 100644 --- a/SpineViewer/Dialogs/ConvertFileFormatDialog.cs +++ b/SpineViewer/Dialogs/ConvertFileFormatDialog.cs @@ -59,13 +59,13 @@ namespace SpineViewer.Dialogs } } - if (sourceVersion != Spine.Version.Auto && !SkeletonConverter.ImplementedVersions.Contains(sourceVersion)) + if (sourceVersion != Spine.Version.Auto && !SkeletonConverter.HasImplementation(sourceVersion)) { MessageBox.Info($"{sourceVersion.GetName()} 版本尚未实现(咕咕咕~)"); return; } - if (!SkeletonConverter.ImplementedVersions.Contains(targetVersion)) + if (!SkeletonConverter.HasImplementation(targetVersion)) { MessageBox.Info($"{targetVersion.GetName()} 版本尚未实现(咕咕咕~)"); return; diff --git a/SpineViewer/Dialogs/OpenSpineDialog.cs b/SpineViewer/Dialogs/OpenSpineDialog.cs index e754998..d270855 100644 --- a/SpineViewer/Dialogs/OpenSpineDialog.cs +++ b/SpineViewer/Dialogs/OpenSpineDialog.cs @@ -79,7 +79,7 @@ namespace SpineViewer.Dialogs atlasPath = Path.GetFullPath(atlasPath); } - if (version != Spine.Version.Auto && !Spine.Spine.ImplementedVersions.Contains(version)) + if (version != Spine.Version.Auto && !Spine.Spine.HasImplementation(version)) { MessageBox.Info($"{version.GetName()} 版本尚未实现(咕咕咕~)"); return; diff --git a/SpineViewer/Exporter/ExportArgs.cs b/SpineViewer/Exporter/ExportArgs.cs index 077d41e..c0ba23b 100644 --- a/SpineViewer/Exporter/ExportArgs.cs +++ b/SpineViewer/Exporter/ExportArgs.cs @@ -14,40 +14,18 @@ namespace SpineViewer.Exporter /// /// 导出参数基类 /// - public abstract class ExportArgs + public abstract class ExportArgs : ImplementationResolver { - /// - /// 实现类缓存 - /// - private static readonly Dictionary ImplementationTypes = []; - - static ExportArgs() - { - var impTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => typeof(ExportArgs).IsAssignableFrom(t) && !t.IsAbstract); - foreach (var type in impTypes) - { - var attr = type.GetCustomAttribute(); - if (attr is not null) - { - if (ImplementationTypes.ContainsKey(attr.ExportType)) - throw new InvalidOperationException($"Multiple implementations found: {attr.ExportType}"); - ImplementationTypes[attr.ExportType] = type; - } - } - Program.Logger.Debug("Find export args implementations: [{}]", string.Join(", ", ImplementationTypes.Keys)); - } - /// /// 创建指定类型导出参数 /// + /// 导出类型 + /// 分辨率 + /// 导出视图 + /// 仅渲染选中 + /// 返回与指定 匹配的导出参数实例 public static ExportArgs New(ExportType exportType, Size resolution, SFML.Graphics.View view, bool renderSelectedOnly) - { - if (!ImplementationTypes.TryGetValue(exportType, out var type)) - { - throw new NotImplementedException($"Not implemented type: {exportType}"); - } - return (ExportArgs)Activator.CreateInstance(type, resolution, view, renderSelectedOnly); - } + => New(exportType, resolution, view, renderSelectedOnly); public ExportArgs(Size resolution, SFML.Graphics.View view, bool renderSelectedOnly) { diff --git a/SpineViewer/Exporter/ExportHelper.cs b/SpineViewer/Exporter/ExportHelper.cs index c75fa49..ec889e3 100644 --- a/SpineViewer/Exporter/ExportHelper.cs +++ b/SpineViewer/Exporter/ExportHelper.cs @@ -26,14 +26,9 @@ namespace SpineViewer.Exporter /// 导出实现类标记 /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public class ExportImplementationAttribute : Attribute + public class ExportImplementationAttribute(ExportType exportType) : Attribute, IImplementationKey { - public ExportType ExportType { get; } - - public ExportImplementationAttribute(ExportType exportType) - { - ExportType = exportType; - } + public ExportType ImplementationKey { get; private set; } = exportType; } /// diff --git a/SpineViewer/Exporter/Exporter.cs b/SpineViewer/Exporter/Exporter.cs index 018ea65..298e420 100644 --- a/SpineViewer/Exporter/Exporter.cs +++ b/SpineViewer/Exporter/Exporter.cs @@ -12,56 +12,25 @@ namespace SpineViewer.Exporter /// /// 导出器基类 /// - public abstract class Exporter + public abstract class Exporter(ExportArgs exportArgs) : ImplementationResolver { /// - /// 实现类缓存 + /// 创建指定类型导出器 /// - private static readonly Dictionary ImplementationTypes = []; - - static Exporter() - { - var impTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => typeof(Exporter).IsAssignableFrom(t) && !t.IsAbstract); - foreach (var type in impTypes) - { - var attr = type.GetCustomAttribute(); - if (attr is not null) - { - if (ImplementationTypes.ContainsKey(attr.ExportType)) - throw new InvalidOperationException($"Multiple implementations found: {attr.ExportType}"); - ImplementationTypes[attr.ExportType] = type; - } - } - Program.Logger.Debug("Find exporter implementations: [{}]", string.Join(", ", ImplementationTypes.Keys)); - } - - /// - /// 创建指定类型导出参数 - /// - public static Exporter New(ExportType exportType, ExportArgs exportArgs) - { - if (!ImplementationTypes.TryGetValue(exportType, out var type)) - { - throw new NotImplementedException($"Not implemented type: {exportType}"); - } - return (Exporter)Activator.CreateInstance(type, exportArgs); - } + /// 导出类型 + /// 匹配的导出参数 + /// 匹配的导出器 + public static Exporter New(ExportType exportType, ExportArgs exportArgs) => New(exportType, exportArgs); /// /// 导出参数 /// - public ExportArgs ExportArgs { get; } + public ExportArgs ExportArgs { get; } = exportArgs; /// /// 可用于文件名的时间戳字符串 /// - protected readonly string timestamp; - - public Exporter(ExportArgs exportArgs) - { - ExportArgs = exportArgs; - timestamp = DateTime.Now.ToString("yyMMddHHmmss"); - } + protected readonly string timestamp = DateTime.Now.ToString("yyMMddHHmmss"); /// /// 获取供渲染的 SFML.Graphics.RenderTexture diff --git a/SpineViewer/ImplementationResolver.cs b/SpineViewer/ImplementationResolver.cs new file mode 100644 index 0000000..bb2f23e --- /dev/null +++ b/SpineViewer/ImplementationResolver.cs @@ -0,0 +1,67 @@ +using SpineViewer.Exporter; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using static SFML.Window.Keyboard; + +namespace SpineViewer +{ + public interface IImplementationKey + { + TKey ImplementationKey { get; } + } + + /// + /// 可以使用反射查找基类关联的所有实现类 + /// + /// 所有实现类的基类型 + /// 实现类类型属性标记类型 + /// /// 实现类类型标记类型 + public abstract class ImplementationResolver where TAttr : Attribute, IImplementationKey + { + /// + /// 实现类型缓存 + /// + private static readonly Dictionary ImplementationTypes = new(); + + static ImplementationResolver() + { + var baseType = typeof(TBase); + var impTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => baseType.IsAssignableFrom(t) && !t.IsAbstract); + foreach (var type in impTypes) + { + var attr = type.GetCustomAttribute(); + if (attr is not null) + { + var key = attr.ImplementationKey; + if (ImplementationTypes.ContainsKey(key)) + throw new InvalidOperationException($"Multiple implementations found for key: {key}"); + ImplementationTypes[key] = type; + } + } + Program.Logger.Debug("Found implementations for {}: {}", baseType, string.Join(", ", ImplementationTypes.Keys)); + } + + /// + /// 判断某种类型是否实现 + /// + public static bool HasImplementation(TKey key) => ImplementationTypes.ContainsKey(key); + + /// + /// 根据实现类键和参数创建实例 + /// + /// + /// + /// + /// + protected static TBase New(TKey impKey, params object?[]? args) + { + if (!ImplementationTypes.TryGetValue(impKey, out var type)) + throw new NotImplementedException($"Not implemented type for {typeof(TBase)}: {impKey}"); + return (TBase)Activator.CreateInstance(type, args); + } + } +} diff --git a/SpineViewer/Spine/SkeletonConverter.cs b/SpineViewer/Spine/SkeletonConverter.cs index b675e3d..b3faf57 100644 --- a/SpineViewer/Spine/SkeletonConverter.cs +++ b/SpineViewer/Spine/SkeletonConverter.cs @@ -15,46 +15,12 @@ namespace SpineViewer.Spine /// /// SkeletonConverter 基类, 使用静态方法 New 来创建具体版本对象 /// - public abstract class SkeletonConverter + public abstract class SkeletonConverter : ImplementationResolver { - /// - /// 实现类缓存 - /// - private static readonly Dictionary ImplementationTypes = []; - public static readonly Dictionary.KeyCollection ImplementedVersions; - - /// - /// 静态构造函数 - /// - static SkeletonConverter() - { - // 遍历并缓存标记了 SpineImplementationAttribute 的类型 - var impTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => typeof(SkeletonConverter).IsAssignableFrom(t) && !t.IsAbstract); - foreach (var type in impTypes) - { - var attr = type.GetCustomAttribute(); - if (attr is not null) - { - if (ImplementationTypes.ContainsKey(attr.Version)) - throw new InvalidOperationException($"Multiple implementations found: {attr.Version}"); - ImplementationTypes[attr.Version] = type; - } - } - Program.Logger.Debug("Find SkeletonConverter implementations: [{}]", string.Join(", ", ImplementationTypes.Keys)); - ImplementedVersions = ImplementationTypes.Keys; - } - /// /// 创建特定版本的 SkeletonConverter /// - public static SkeletonConverter New(Version version) - { - if (!ImplementationTypes.TryGetValue(version, out var cvterType)) - { - throw new NotImplementedException($"Not implemented version: {version}"); - } - return (SkeletonConverter)Activator.CreateInstance(cvterType); - } + public static SkeletonConverter New(Version version) => New(version); /// /// Json 格式控制 diff --git a/SpineViewer/Spine/Spine.cs b/SpineViewer/Spine/Spine.cs index e6741f0..fcf5751 100644 --- a/SpineViewer/Spine/Spine.cs +++ b/SpineViewer/Spine/Spine.cs @@ -21,7 +21,7 @@ namespace SpineViewer.Spine /// /// Spine 基类, 使用静态方法 New 来创建具体版本对象 /// - public abstract class Spine : SFML.Graphics.Drawable, IDisposable + public abstract class Spine : ImplementationResolver, SFML.Graphics.Drawable, IDisposable { /// /// 常规骨骼文件后缀集合 @@ -48,12 +48,6 @@ namespace SpineViewer.Spine /// public const float SCALE_MIN = 0.001f; - /// - /// 实现类缓存 - /// - private static readonly Dictionary ImplementationTypes = []; - public static readonly Dictionary.KeyCollection ImplementedVersions; - /// /// 用于解决 PMA 和渐变动画问题的片段着色器 /// @@ -74,21 +68,6 @@ namespace SpineViewer.Spine /// static Spine() { - // 遍历并缓存标记了 SpineImplementationAttribute 的类型 - var impTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => typeof(Spine).IsAssignableFrom(t) && !t.IsAbstract); - foreach (var type in impTypes) - { - var attr = type.GetCustomAttribute(); - if (attr is not null) - { - if (ImplementationTypes.ContainsKey(attr.Version)) - throw new InvalidOperationException($"Multiple implementations found: {attr.Version}"); - ImplementationTypes[attr.Version] = type; - } - } - Program.Logger.Debug("Find Spine implementations: [{}]", string.Join(", ", ImplementationTypes.Keys)); - ImplementedVersions = ImplementationTypes.Keys; - // 加载 FragmentShader try { @@ -178,12 +157,7 @@ namespace SpineViewer.Spine else throw new InvalidDataException($"Auto version detection failed for {skelPath}, try to use a specific version"); } - if (!ImplementationTypes.TryGetValue(version, out var spineType)) - { - throw new NotImplementedException($"Not implemented version: {version}"); - } - - var spine = (Spine)Activator.CreateInstance(spineType, skelPath, atlasPath); + var spine = New(version, skelPath, atlasPath); // 统一初始化 spine.initBounds = spine.Bounds; @@ -233,7 +207,7 @@ namespace SpineViewer.Spine atlasPath ??= Path.ChangeExtension(skelPath, ".atlas"); // 设置 Version - Version = attr.Version; + Version = attr.ImplementationKey; AssetsDir = Directory.GetParent(skelPath).FullName; SkelPath = Path.GetFullPath(skelPath); AtlasPath = Path.GetFullPath(atlasPath); diff --git a/SpineViewer/Spine/Version.cs b/SpineViewer/Spine/SpineHelper.cs similarity index 94% rename from SpineViewer/Spine/Version.cs rename to SpineViewer/Spine/SpineHelper.cs index ee57bc7..3944aee 100644 --- a/SpineViewer/Spine/Version.cs +++ b/SpineViewer/Spine/SpineHelper.cs @@ -29,14 +29,9 @@ namespace SpineViewer.Spine /// Spine 实现类标记 /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public class SpineImplementationAttribute : Attribute + public class SpineImplementationAttribute(Version version) : Attribute, IImplementationKey { - public Version Version { get; } - - public SpineImplementationAttribute(Version version) - { - Version = version; - } + public Version ImplementationKey { get; private set; } = version; } ///