【发布时间】:2015-10-26 17:00:16
【问题描述】:
澄清一下,我使用动态和 MakeGenericType 进行了这项工作。但我不禁认为有更好的方法来做到这一点。我想做的是使用 Unity 创建一个“插件”加载器。我将在发布代码时对其进行解释,以便您了解我在做什么。
首先我将发布插件本身:
[RegisterAction("MyPlugin", typeof(bool), typeof(MyPlugin))]
public class MyPlugin: IStrategy<bool>
{
public IStrategyResult<bool> Execute(ISerializable info = null)
{
bool result;
try
{
// do stuff
result = true;
}
catch (Exception)
{
result = false;
}
return new StrategyResult<bool>
{
Value = result
};
}
}
这里有几点需要注意。首先是RegisterActionAttribute:
[AttributeUsage(AttributeTargets.Class)]
public sealed class RegisterActionAttribute : Attribute
{
public StrategyAction StrategyAction { get; }
public RegisterActionAttribute(string actionName, Type targetType, Type returnType, params string[] depdencies)
{
StrategyAction = new StrategyAction
{
Name = actionName,
StrategyType = targetType,
ResponseType = returnType,
Dependencies = depdencies
};
}
}
然后是接口:
public interface IStrategy<T>
{
IStrategyResult<T> Execute(ISerializable info = null);
}
public interface IStrategyResult<T>
{
bool IsValid { get; set; }
T Value { get; set; }
}
一切都相当简单。这里的目标只是在加载类时将一些元数据附加到类。加载通过统一使用包装器进行,该包装器使用文件搜索模式简单地加载 bin 目录中的程序集,并将其添加到具有 StrategyActions 集合的单例类中。我不需要在这里粘贴所有统一代码,因为我知道它可以工作并注册和解析程序集。
现在进入问题的核心。我在单例上有一个执行动作的函数。这些与 Unity.Interception HandlerAttributes 一起应用并像这样传递了一个字符串(我可以为此发布代码,但我认为它不相关):
[ExecuteAction("MyPlugin")]
处理程序在单例类上调用以下执行函数来“执行”已注册(添加到集合中)的函数。
public dynamic Execute(string action, params object[] parameters)
{
var strategyAction = _registeredActions.FirstOrDefault(a => a.Name == action);
if (strategyAction == null)
return null;
var type = typeof (IStrategy<>);
var generic = type.MakeGenericType(strategyAction.StrategyType);
var returnType = typeof (IStrategyResult<>);
var genericReturn = returnType.MakeGenericType(strategyAction.ResponseType);
var instance = UnityManager.Container.Resolve(generic, strategyAction.Name);
var method = instance.GetType().GetMethod("Execute");
return method.Invoke(instance, parameters);
}
这个执行被包装在一个枚举器调用中,它返回一个结果集合,它排序来管理依赖关系和什么不是(见下文)。调用者使用 ISTrategyResult{T} 的 Value 属性引用这些值来执行其他业务规则定义的各种事情。
public List<dynamic> ExecuteQueuedActions()
{
var results = new List<dynamic>();
var actions = _queuedActions.AsQueryable();
var sortedActions = TopologicalSort.Sort(actions, action => action.Dependencies, action => action.Name);
foreach(var strategyAction in sortedActions)
{
_queuedActions.Remove(strategyAction);
results.Add(Execute(strategyAction.Name));
}
return results;
}
现在请注意,这可行,并且我得到了插件 RegisterAction 属性指定的返回类型。如您所见,我正在捕获插件的类型和返回类型。我正在使用“通用”变量通过使用 MakeGenericType 来统一解析类型,效果很好。我还根据集合中的类型创建一个表示返回类型的泛型。
我不喜欢这里必须使用动态来将此值返回给函数。我想不出一种方法将它作为 IStrategyResult{T} 返回,因为显然“动态执行(...”的调用者在运行时不能暗示函数的返回类型。我仔细考虑了使用 MakeGenericMethod 调用对 Execute 的调用,因为我实际上具有 StrategyAction 的预期类型。如果我能弄清楚在调用期间确定 T 的类型时如何返回 IStrategyResult {T} 的强类型结果,那就太酷了.
我明白为什么我不能用我当前的实现来做到这一点我只是想找到一种方法来包装所有这些功能而不使用动态。并希望有人可以提供一些可能有用的建议。如果这意味着将其与对非泛型类的其他调用或类似的东西一起包装,那么如果这是唯一的解决方案,那也很好。
【问题讨论】:
-
获得结果后如何处理?看起来您需要一些相应的
IHandler<T>接口来处理结果。然后您可以将Execute和Handle方法组合为Actions。 -
我添加了一个包装函数,我可以使用它来聚合调用结果,希望能回答你的问题。还是我误解了你的回答?
-
我想我明白你在说什么,这是有道理的。但是,鉴于我需要返回其中一些值,我认为会遇到相同的问题,即知道调用者中 T 的类型。我知道这是这样做的固有问题。我想这只是野兽的本性,我唯一能做的就是在调用者中添加一些检查/强制转换以适当地处理结果。关于动态的酷(但危险)的事情是我不需要强制转换来引用结果。但一旦它处于动态状态,它就会处于哲学上的“灰色”区域。
-
您如何汇总结果?你在检查 IStrategyResult
中 T 的类型吗?如果你能提供一个样本就好了。 -
您能否举例说明
ExecuteQueuedActions的调用者希望对结果执行什么操作?IStrategy<T>中的T是否应该以某种方式受到约束或限制为一组固定的类型?
标签: c# .net generics reflection