【问题标题】:Get assembly of code that instantiated object (with inheritance)获取实例化对象的代码程序集(具有继承)
【发布时间】:2015-03-15 21:39:35
【问题描述】:

我有一个抽象类,它需要能够加载程序集中包含的文件,这些文件构成了该类的对象。我可以为我班级的每个孩子使用FileAssembly = Assembly.GetCallingAssembly(),但由于它是一个人们可以扩展的库,我希望在不需要他们这样做的情况下发生这种情况。

我现在的设置大致如下:

public abstract class ExternalResource : Resource, IDisposable
{
    public string File { get; private set; }
    protected Assembly FileAssembly { get; set; }

    protected ExternalResource(string id, string file)
        : base(id)
    {
        File = file;
    }

    //and s'more code
}

public class Sound : ExternalResource
{
    public Sound (string id, string file)
        : base(id, file)
    {
        //this is the line I want to be able to get rid of
        FileAssembly = Assembly.GetCallingAssembly();
    }
}

使用我的库的人可以在不设置 FileAssembly 的情况下创建自己的 ExternalResource,这是不可取的。如果他们要从已经从 ExternalResource 继承的类继承,那将变得非常混乱。如何获得实例化对象的代码程序集?在不对现有系统进行太多更改的情况下解决它的任何其他方法也将不胜感激!

【问题讨论】:

  • 你需要调用代码的程序集,还是定义派生类型的程序集?
  • 调用代码,这个想法很好,无论文件提供到哪里,都必须是解析的程序集。

标签: c# .net inheritance assembly-resolution


【解决方案1】:

您可以使用 System.Diagnostics 命名空间中的 StackTrace 类:

在 ExternalResource 类的构造函数中:

var stack = new StackTrace(true);
var thisFrame = stack.GetFrame(0); // ExternalResource constructor
var parentFrame = stack.GetFrame(1); // Sound constructor
var grandparentFrame = stack.GetFrame(2); // This is the one!

var invokingMethod = grandparentFrame.GetMethod();
var callingAssembly = invokingMethod.Module.Assembly;

对 thisFrame 和 parentFrame 的引用只是为了帮助理解。我建议您实际上走上堆栈帧并更稳健地执行它,而不是仅仅假设它始终是您想要的第 2 帧(如果您有额外的子类,这不会给出正确的答案)。

【讨论】:

  • 声音可以被再次继承,所以不清楚对象实例化了多少层。
  • 如果我创建一个继承自SoundExtendedSound 类,等等?
  • 有趣的是,当您发表评论时,我正在编辑该帖子 :-) 遍历堆栈直到找到一个不是构造函数的方法可能是最简单的或者不是在具有基本类型 ExternalResource 的类型中。
  • 我不确定这是否可行,因为您可以在构造函数中创建 ExternalResource(甚至是从 ExternalResource 继承的不同类型的构造函数)?
  • @Villermen 将示例代码放入循环中并检查invokingMethod 以查看其声明类型是否可分配给ExternalResource
【解决方案2】:

使用调用程序集可能会让某些人感到沮丧,他们将他们的 ExternalResource 相关代码移动到新的单独程序集中,而没有意识到它具有程序集依赖关系,或者想要将他们的资源移动到单独标识的程序集中(例如,用于本地化)。

我的建议是:

  • 向构造函数添加显式程序集参数,使其非常清楚它正在对某些程序集执行某些操作。
  • 为方便用户提供确定调用程序集的工厂方法

如果您的所有派生类型都需要构造函数参数string id, string file,然后是新的显式程序集参数,那么您的工厂方法可能是通用的。比如:

public static TExternalResource CreateExternalResource<TExternalResource>(string id, string file, Assembly assembly = null) where TExternalResource : ExternalResource
{

    var ResolvedAssembly = assembly ?? Assembly.GetCallingAssembly();

    return Activator.CreateInstance(typeof(TExternalResource), id, file, ResolvedAssembly) as TExternalResource;
}

【讨论】:

  • 我的想法是让它透明地发生,因为我希望清楚的是,无论提供文件的地方应该是它签入的程序集。我也可以让它始终是入口程序集,但我不知道当人们例如添加一个程序集,该程序集使用它自己包含的文件扩展条目程序集。我希望它对使用该库的人来说尽可能简单(否则他们无论如何都会使用相同的代码(当前程序集)。
  • 我了解您想要实现的便利因素,但我仍然认为尝试猜测程序集(并且不提供任何其他选项来代替提供程序集)对于库来说是不好的形式。
猜你喜欢
  • 2014-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-14
  • 2017-01-12
  • 1970-01-01
相关资源
最近更新 更多