提取ImplementationResolver实现

This commit is contained in:
ww-rm
2025-03-29 15:12:50 +08:00
parent 00f5791766
commit b69589394a
10 changed files with 95 additions and 151 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -14,40 +14,18 @@ namespace SpineViewer.Exporter
/// <summary>
/// 导出参数基类
/// </summary>
public abstract class ExportArgs
public abstract class ExportArgs : ImplementationResolver<ExportArgs, ExportImplementationAttribute, ExportType>
{
/// <summary>
/// 实现类缓存
/// </summary>
private static readonly Dictionary<ExportType, Type> 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<ExportImplementationAttribute>();
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));
}
/// <summary>
/// 创建指定类型导出参数
/// </summary>
/// <param name="exportType">导出类型</param>
/// <param name="resolution">分辨率</param>
/// <param name="view">导出视图</param>
/// <param name="renderSelectedOnly">仅渲染选中</param>
/// <returns>返回与指定 <paramref name="exportType"/> 匹配的导出参数实例</returns>
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)
{

View File

@@ -26,14 +26,9 @@ namespace SpineViewer.Exporter
/// 导出实现类标记
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class ExportImplementationAttribute : Attribute
public class ExportImplementationAttribute(ExportType exportType) : Attribute, IImplementationKey<ExportType>
{
public ExportType ExportType { get; }
public ExportImplementationAttribute(ExportType exportType)
{
ExportType = exportType;
}
public ExportType ImplementationKey { get; private set; } = exportType;
}
/// <summary>

View File

@@ -12,56 +12,25 @@ namespace SpineViewer.Exporter
/// <summary>
/// 导出器基类
/// </summary>
public abstract class Exporter
public abstract class Exporter(ExportArgs exportArgs) : ImplementationResolver<Exporter, ExportImplementationAttribute, ExportType>
{
/// <summary>
/// 实现类缓存
/// 创建指定类型导出器
/// </summary>
private static readonly Dictionary<ExportType, Type> 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<ExportImplementationAttribute>();
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));
}
/// <summary>
/// 创建指定类型导出参数
/// </summary>
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);
}
/// <param name="exportType">导出类型</param>
/// <param name="exportArgs">与 <paramref name="exportType"/> 匹配的导出参数</param>
/// <returns>与 <paramref name="exportType"/> 匹配的导出器</returns>
public static Exporter New(ExportType exportType, ExportArgs exportArgs) => New(exportType, exportArgs);
/// <summary>
/// 导出参数
/// </summary>
public ExportArgs ExportArgs { get; }
public ExportArgs ExportArgs { get; } = exportArgs;
/// <summary>
/// 可用于文件名的时间戳字符串
/// </summary>
protected readonly string timestamp;
public Exporter(ExportArgs exportArgs)
{
ExportArgs = exportArgs;
timestamp = DateTime.Now.ToString("yyMMddHHmmss");
}
protected readonly string timestamp = DateTime.Now.ToString("yyMMddHHmmss");
/// <summary>
/// 获取供渲染的 SFML.Graphics.RenderTexture

View File

@@ -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>
{
TKey ImplementationKey { get; }
}
/// <summary>
/// 可以使用反射查找基类关联的所有实现类
/// </summary>
/// <typeparam name="TBase">所有实现类的基类型</typeparam>
/// <typeparam name="TAttr">实现类类型属性标记类型</typeparam>
/// /// <typeparam name="TKey">实现类类型标记类型</typeparam>
public abstract class ImplementationResolver<TBase, TAttr, TKey> where TAttr : Attribute, IImplementationKey<TKey>
{
/// <summary>
/// 实现类型缓存
/// </summary>
private static readonly Dictionary<TKey, Type> 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<TAttr>();
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));
}
/// <summary>
/// 判断某种类型是否实现
/// </summary>
public static bool HasImplementation(TKey key) => ImplementationTypes.ContainsKey(key);
/// <summary>
/// 根据实现类键和参数创建实例
/// </summary>
/// <param name="impKey"></param>
/// <param name="args"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
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);
}
}
}

View File

@@ -15,46 +15,12 @@ namespace SpineViewer.Spine
/// <summary>
/// SkeletonConverter 基类, 使用静态方法 New 来创建具体版本对象
/// </summary>
public abstract class SkeletonConverter
public abstract class SkeletonConverter : ImplementationResolver<SkeletonConverter, SpineImplementationAttribute, Version>
{
/// <summary>
/// 实现类缓存
/// </summary>
private static readonly Dictionary<Version, Type> ImplementationTypes = [];
public static readonly Dictionary<Version, Type>.KeyCollection ImplementedVersions;
/// <summary>
/// 静态构造函数
/// </summary>
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<SpineImplementationAttribute>();
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;
}
/// <summary>
/// 创建特定版本的 SkeletonConverter
/// </summary>
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);
/// <summary>
/// Json 格式控制

View File

@@ -21,7 +21,7 @@ namespace SpineViewer.Spine
/// <summary>
/// Spine 基类, 使用静态方法 New 来创建具体版本对象
/// </summary>
public abstract class Spine : SFML.Graphics.Drawable, IDisposable
public abstract class Spine : ImplementationResolver<Spine, SpineImplementationAttribute, Version>, SFML.Graphics.Drawable, IDisposable
{
/// <summary>
/// 常规骨骼文件后缀集合
@@ -48,12 +48,6 @@ namespace SpineViewer.Spine
/// </summary>
public const float SCALE_MIN = 0.001f;
/// <summary>
/// 实现类缓存
/// </summary>
private static readonly Dictionary<Version, Type> ImplementationTypes = [];
public static readonly Dictionary<Version, Type>.KeyCollection ImplementedVersions;
/// <summary>
/// 用于解决 PMA 和渐变动画问题的片段着色器
/// </summary>
@@ -74,21 +68,6 @@ namespace SpineViewer.Spine
/// </summary>
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<SpineImplementationAttribute>();
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);

View File

@@ -29,14 +29,9 @@ namespace SpineViewer.Spine
/// Spine 实现类标记
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class SpineImplementationAttribute : Attribute
public class SpineImplementationAttribute(Version version) : Attribute, IImplementationKey<Version>
{
public Version Version { get; }
public SpineImplementationAttribute(Version version)
{
Version = version;
}
public Version ImplementationKey { get; private set; } = version;
}
/// <summary>