使用反射,善或恶来注册派生类?

时间:2008-11-25 20:38:53

标签: language-agnostic reflection inheritance

众所周知,当我们派生一个类并使用多态时,某个人,某个地方需要知道要实现什么类。我们可以使用工厂,一个大的switch语句,if-else-if等等。我刚从Bill K那里学到了这个叫做Dependency Injection。

我的问题:使用反射和属性作为依赖注入机制是一种好习惯吗?这样,当我们添加新类型时,列表会动态填充。

这是示例请不要评论如何以其他方式加载图片,我们知道

假设我们有以下IImageFileFormat接口:

public interface IImageFileFormat
{
   string[] SupportedFormats { get; };
   Image Load(string fileName);
   void Save(Image image, string fileName);
}

不同的类将实现此接口:

[FileFormat]
public class BmpFileFormat : IImageFileFormat { ... }

[FileFormat]
public class JpegFileFormat : IImageFileFormat { ... }

当需要加载或保存文件时,管理员需要遍历所有已知的加载器并根据其SupportedExtensions从适当的实例调用Load()/ Save()。

class ImageLoader
{
   public Image Load(string fileName)
   {
      return FindFormat(fileName).Load(fileName);
   }

   public void Save(Image image, string fileName)
   {
      FindFormat(fileName).Save(image, fileName);
   }

   IImageFileFormat FindFormat(string fileName)
   {
      string extension = Path.GetExtension(fileName);
      return formats.First(f => f.SupportedExtensions.Contains(extension));
   }

   private List<IImageFileFormat> formats;
}

我想重要的一点是,是否应该手动或使用反射来填充可用加载器(格式)列表。

手工:

public ImageLoader()
{
   formats = new List<IImageFileFormat>();
   formats.Add(new BmpFileFormat());
   formats.Add(new JpegFileFormat());
}

通过反思:

public ImageLoader()
{
   formats = new List<IImageFileFormat>();
   foreach(Type type in Assembly.GetExecutingAssembly().GetTypes())
   {
      if(type.GetCustomAttributes(typeof(FileFormatAttribute), false).Length > 0)
      {
         formats.Add(Activator.CreateInstance(type))
      }
   }
}

我有时会使用后者而且从来没有发现过这可能是一个非常糟糕的主意。是的,添加新类很容易,但是注册相同类的机制比简单的手工编号列表更难掌握和维护。

请讨论。

5 个答案:

答案 0 :(得分:4)

我的个人偏好都不是 - 当有一个类到某个任意字符串的映射时,配置文件就是这样做的地方恕我直言。这样,您永远不会需要修改代码 - 特别是如果您使用动态加载机制来添加新的动态库。

一般来说,我总是喜欢一些允许我尽可能多地编写代码的方法 - 你的方法都需要改变已编写/构建/部署的代码(因为你的反射路径没有规定添加文件格式加载器新的DLL)。

由Coincoin编辑:

反射方法可以与配置文件有效结合,以找到要注入的实施方案。

  • 可以使用规范名称在配置文件中明确声明类型,类似于MSBuild&lt; UsingTask&gt;
  • 配置可以找到程序集,但是我们必须注入所有匹配的类型,ala Microsoft Visual Studio Packages。
  • 将值或条件集匹配到所需类型的任何其他机制。

答案 1 :(得分:3)

我的投票是反射方法更好。使用该方法,添加新文件格式只会修改代码的一部分 - 您定义类来处理文件格式的位置。没有反射,你必须记住修改另一个类,ImageLoader,以及

答案 2 :(得分:1)

这几乎不是依赖注入模式的全部内容吗?

如果您可以隔离依赖关系,那么机制几乎肯定会基于反射,但它将是配置文件驱动的,因此反射的混乱可以很好地封装和隔离。

我相信DI你只是说我需要一个<interface>类型的对象和一些其他参数,DI系统会向你返回一个满足你条件的对象。

这与IoC(控制反转)一起使用,其中提供的对象可能需要其他东西,以便在将对象返回给用户之前自动创建并安装到对象中(由DI创建)。

答案 3 :(得分:0)

我知道这与“没有关于以其他方式加载图像的评论”相关,但为什么不只是翻转你的依赖项 - 而不是让ImageLoader依赖于ImageFileFormats,让每个IImageFileFormat依赖于ImageLoader?你会从中获得一些东西:

  • 每次添加新的IImageFileFormat时,您都不需要在其他地方进行任何更改(并且您也不必使用反射)。
  • 如果你更进一步抽象ImageLoader,你可以在单元测试中模拟它,使得更容易测试每个IImageFileFormat的具体实现

答案 4 :(得分:0)

在vb.net中,如果所有图像加载器都在同一个程序集中,可以使用部分类和事件来实现所需的效果(有一个类的目的是在图像加载器应该注册自己时触发事件;每个包含图像加载器的文件都可以使用“部分类”向该类添加另一个事件处理程序); C#没有直接等同于vb.net的WithEvents语法,但我怀疑部分类是实现相同功能的有限机制。