将泛型类型的实例返回到在运行时解析的函数

Bra*_*don 23 .net c# generics reflection

只是为了澄清,我使用动态和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
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里要注意的事情.首先是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
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

那么接口:

public interface IStrategy<T>
{
    IStrategyResult<T> Execute(ISerializable info = null);
}

public interface IStrategyResult<T>
{
    bool IsValid { get; set; }
    T Value { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

一切都相当直接.这里的目标只是在加载时将一些元数据附加到类中.使用包装器通过统一进行加载,该包装器使用文件搜索模式简单地将组件加载到bin目录中,并将其添加到具有StrategyActions集合的单个类中.我不需要粘贴所有统一代码,因为我知道它可以工作并注册和解析程序集.

所以现在问题的关键.我在执行动作的单例上有一个函数.这些是应用Unity.Interception HandlerAttributes并传递一个像这样的字符串(我可以为此发布代码,但我认为它不相关):

[ExecuteAction("MyPlugin")]
Run Code Online (Sandbox Code Playgroud)

处理程序在singleton类上调用以下execute函数来"执行"已注册(添加到集合)的函数.

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);
}
Run Code Online (Sandbox Code Playgroud)

此执行包含在枚举器调用中,该调用返回结果集合,这些结果用于管理依赖关系以及不依赖关系(见下文).调用者使用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;
    }
Run Code Online (Sandbox Code Playgroud)

现在请注意,这是有效的,我得到插件RegisterAction属性指定的返回类型.正如您所看到的,我正在捕获插件的类型和返回类型.我正在使用"泛型"变量通过使用MakeGenericType来解析具有单位的类型,这很好.我还基于集合中的类型创建表示返回类型的泛型.

我不喜欢这里是必须使用动态将此值返回到函数.我无法找到一种方法将其作为IStrategyResult {T}返回,因为显然"动态执行(..."的调用者在运行时不能暗示函数的返回类型.我仔细研究了使用MakeGenericMethod调用对Execute的调用,因为我实际上具有预期类型的​​StrategyAction.如果我能够在调用期间确定T类型的同时如何计算出返回IStrategyResult {T}的强类型结果,那将会很酷. .

我明白为什么我不能用我当前的实现做到这一点我只是想找到一种方法来包装所有这些功能而不使用动态.并希望有人可以提供一些可能有用的建议.如果这意味着将其与其他对非泛型类或类似类的调用包装起来,那么如果这是唯一的解决方案那也没关系.

Ste*_*rov 7

你需要一个更彻底的重构,而不仅仅是弄清楚如何调用你的插件.

不需要该[RegisterAction]属性来保存targetType和returnType,这些属性参数很容易与代码不同步,使它们成为潜在的漏洞.

然后从你的设置的另一面思考:你如何使用数据,你如何使用IStrategyResult<>它 - 它真的必须是通用的还是有一种特定的方法可以封装结果的类型?我无法想象一个插件系统会向主机返回"任何东西".提示真的在你的dynamic Execute(...)- 你的参数和你的结果都失去了他们强大的打字,告诉你强插入插件没有帮助任何东西.只需使用object或 - 更好 - 创建一个StrategyResult类而不是当前的接口,并提供那里所需的任何属性(我添加了一些无聊的例子),例如:

public class StrategyResult{
  public object Result{get;set;}
  public Type ResultType {get;set;}

  // frivolous examples
  public bool IsError {get;set;}
  public string ErrorMessage {get;set;}

  // really off-the-wall example
  public Func<StrategyHostContext,bool> ApplyResultToContext {get;set;}

  public StrategyResult(){
  }

  public StrategyResult FromStrategy(IStrategy strategy){
    return new StrategyResult{
      ResultType = strategy.ResultType
    } 
  }

  public StrategyResult FromStrategyExecute(IStrategy strategy, ISerializable info = null){
     var result = FromStrategy(strategy);
     try{
       strategy.Execute(info);
     } catch (Exception x){
       result.IsError = true;
       result.ErrorMessage = x.Message;
     }
  }
}
Run Code Online (Sandbox Code Playgroud)

然后你IStrategy变成:

public interface IStrategy{
  Type ResultType {get;}
  void Initialize(SomeContextClassMaybe context);
  StrategyResult Execute(ISerializable info = null); 
}
Run Code Online (Sandbox Code Playgroud)

您还可以更改属性以使其更有效地加载大型插件:

[AttributeUsage(AttributeTargets.Assembly)]
public sealed class AddinStrategyAttribute : Attribute
{
  public Type StategyType {get; private set;}
  public AddinStrategyAttribute(Type strategyType){
   StrategyType = strategyType;
  }
}
Run Code Online (Sandbox Code Playgroud)

...并使用如下属性:

[assembly:AddinStrategy(typeof(BoolStrategy))] // note it's outside the namespace
namespace MyNamespace{
    public class BoolStrategy: IStrategy{
      public Type ResultType { get{ return typeof(bool);}}
      public void Initialize (SomeContextClassMaybe context){
      }
      public StrategyResult Execute(ISerializable info = null){
        return StrategyResult.FromStrategyExecute(this,info);
      }
    }
}
Run Code Online (Sandbox Code Playgroud)


Tyr*_*son 5

假设调用者对任何插件或结果ExecuteActions没有任何了解T并且必须使用dynamic或者object无论如何,那么以下可能有效:

基础设施:

public interface IStrategy
{
    IStrategyResult Execute(ISerializable info = null);
}

public interface IStrategyResult
{
    bool IsValid { get; }
    dynamic Value { get; }
}

public class StrategyResult<T> : IStrategyResult
{
    public T Value { get; private set; }
    public StrategyResult(T value) { this.Value = value; }

    public bool IsValid { get { throw new NotImplementedException(); } }

    dynamic IStrategyResult.Value { get { return this.Value; } }

}

[AttributeUsage(AttributeTargets.Class)]
public sealed class RegisterActionAttribute : Attribute
{
    public List<string> Dependencies { get; private set; }

    public RegisterActionAttribute(params string[] depdencies)
    {
        this.Dependencies = new List<string>(depdencies);
    }
}

public class StrategyAction
{
    public string Name;
    public List<string> Dependencies;
}

public abstract class BasePlugin<T> : IStrategy
{
    public IStrategyResult Execute(ISerializable info = null)
    {
        return new StrategyResult<T>(this.execute(info));
    }
    protected abstract T execute(ISerializable info);
}
Run Code Online (Sandbox Code Playgroud)

示例插件:

[RegisterAction]
public class MyFirstPlugin: BasePlugin<bool>
{
    protected override bool execute(ISerializable info = null)
    {
        try
        {
           // do stuff
           return true;
        }
        catch (Exception)
        {
            return false;
        }
    }
}

[RegisterAction("MyFirstPlugin")]
public class MySecondPlugin: BasePlugin<string>
{
    protected override string execute(ISerializable info = null)
    {
        try
        {
           // do stuff
           return "success";
        }
        catch (Exception)
        {
            return "failed";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

示例执行引擎:

public class Engine
{

    public  List<StrategyAction>    registeredActions   = new List<StrategyAction>();
    private List<StrategyAction>    queuedActions       = new List<StrategyAction>();

    public IStrategyResult Execute(string action, ISerializable info = null)
    {
        if (this.registeredActions.FirstOrDefault(a=>a.Name == action) == null) return null;

        // This code did not appear to be used anyway
        //var returnType = typeof (IStrategyResult<>);                                              //var genericReturn = returnType.MakeGenericType(strategyAction.ResponseType);

        var instance = (IStrategy) UnityManager.Container.Resolve(typeof(IStrategy), action);

        return instance.Execute(info);
    }

    public List<IStrategyResult> ExecuteQueuedActions()
    {
        var results         = new List<IStrategyResult>();
        var actions         = this.queuedActions.AsQueryable();
        var sortedActions = TopologicalSort.Sort(actions, action => action.Dependencies, action => action.Name);
        foreach(var strategyAction in sortedActions)
        {
            this.queuedActions.Remove(strategyAction);
            results.Add(Execute(strategyAction.Name));
        }
        return results;
    }

}
Run Code Online (Sandbox Code Playgroud)

请注意,当加载插件时,RegisterActionAttribute信息以及加​​载的插件类型的名称需要组合到一个StrategyAction实例中并加载到registeredActions引擎的字段中.

以上允许插件使用强类型,但仍然允许引擎处理各种类型.如果您需要引擎使用更强类型的数据,那么请提供一个示例,说明调用者应该如何ExecuteQueuedActions处理结果ExecuteQueuedActions.