Language agnostic 用反射注册派生类,好还是坏?

Language agnostic 用反射注册派生类,好还是坏?,language-agnostic,reflection,inheritance,Language Agnostic,Reflection,Inheritance,众所周知,当我们派生一个类并使用多态性时,某个地方的某个人需要知道实例化哪个类。我们可以使用工厂、大开关语句、if-else-if等等。我刚刚从Bill K那里学到了这叫做依赖注入 我的问题:使用反射和属性作为依赖项注入机制是否是一种良好的做法?这样,当我们添加新类型时,列表会动态填充 下面是一个示例。请不要评论如何加载图像可以做其他方式,我们知道 假设我们有以下IImageFileFormat接口: public interface IImageFileFormat { string[]

众所周知,当我们派生一个类并使用多态性时,某个地方的某个人需要知道实例化哪个类。我们可以使用工厂、大开关语句、if-else-if等等。我刚刚从Bill K那里学到了这叫做依赖注入

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

下面是一个示例。请不要评论如何加载图像可以做其他方式,我们知道

假设我们有以下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 { ... }
不同的类将实现此接口:

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 { ... }
当需要加载或保存文件时,管理器需要遍历所有已知的加载程序,并根据其支持的扩展从相应的实例调用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;
}
类图像加载器
{
公共图像加载(字符串文件名)
{
返回FindFormat(文件名).Load(文件名);
}
公共void保存(图像、字符串文件名)
{
FindFormat(文件名).Save(图像,文件名);
}
IImageFileFormat FindFormat(字符串文件名)
{
字符串扩展名=Path.GetExtension(文件名);
首先(f=>f.SupportedExtensions.Contains(extension));
}
私有列表格式;
}
我想这里重要的一点是,可用加载程序(格式)的列表应该手动填充还是使用反射填充

手工:

public ImageLoader()
{
   formats = new List<IImageFileFormat>();
   formats.Add(new BmpFileFormat());
   formats.Add(new JpegFileFormat());
}
公共图像加载器() { 格式=新列表(); 添加(新的BmpFileFormat()); 添加(新的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))
      }
   }
}
公共图像加载器() { 格式=新列表(); foreach(在Assembly.getExecutionGassembly().GetTypes()中键入类型) { if(type.GetCustomAttributes(typeof(FileFormatAttribute),false).Length>0) { formats.Add(Activator.CreateInstance(类型)) } } } 我有时用后者,但我从来没有想到这可能是一个非常糟糕的主意。是的,添加新类很容易,但是注册这些相同类的机制比简单的手工编码列表更难掌握和维护


请讨论。

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

我个人的偏好是两者都不是-当类映射到某个任意字符串时,配置文件就是这样做的地方。这样,您就永远不需要修改代码,尤其是在使用动态加载机制添加新的动态库时

一般来说,我总是喜欢一些方法,它允许我尽可能多地编写一次代码——这两种方法都需要修改已经编写/构建/部署的代码(因为您的反射路由没有为在新DLL中添加文件格式加载器做任何准备)

按硬币编辑:

反射方法可以有效地与配置文件相结合,以定位要注入的实现

  • 可以使用规范名称在配置文件中明确声明该类型,类似于MSBuild
  • 配置可以定位程序集,但是我们必须注入所有匹配的类型,比如Microsoft Visual Studio包
  • 将值或条件集与所需类型匹配的任何其他机制

我知道这与“对以其他方式加载图像不发表评论”有关,但为什么不直接翻转您的依赖关系呢?与其让ImageLoader依赖ImageFileFormats,不如让每个IIImageFileFormat依赖ImageLoader?您将从中获得一些好处:

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

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

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

我相信对于DI,您只需说我需要一个类型为
的对象以及一些其他参数,DI系统就会向您返回一个满足您条件的对象


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

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

我认为这种方法的唯一困难在于它意味着重新部署代码,而不是以插件方式添加额外的模块。诚然,重新部署并不总是糟糕或困难的——但对于一般情况,如果可能的话,我宁愿避免。我不确定我是否理解你的答案。文件格式首先是如何创建的?创建ImageFileFormat的那个东西,它怎么知道它存在?很好!然而,它需要在不同的地方写东西(