Language agnostic 用反射注册派生类,好还是坏?
众所周知,当我们派生一个类并使用多态性时,某个地方的某个人需要知道实例化哪个类。我们可以使用工厂、大开关语句、if-else-if等等。我刚刚从Bill K那里学到了这叫做依赖注入 我的问题:使用反射和属性作为依赖项注入机制是否是一种良好的做法?这样,当我们添加新类型时,列表会动态填充 下面是一个示例。请不要评论如何加载图像可以做其他方式,我们知道 假设我们有以下IImageFileFormat接口:Language agnostic 用反射注册派生类,好还是坏?,language-agnostic,reflection,inheritance,Language Agnostic,Reflection,Inheritance,众所周知,当我们派生一个类并使用多态性时,某个地方的某个人需要知道实例化哪个类。我们可以使用工厂、大开关语句、if-else-if等等。我刚刚从Bill K那里学到了这叫做依赖注入 我的问题:使用反射和属性作为依赖项注入机制是否是一种良好的做法?这样,当我们添加新类型时,列表会动态填充 下面是一个示例。请不要评论如何加载图像可以做其他方式,我们知道 假设我们有以下IImageFileFormat接口: public interface IImageFileFormat { string[]
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包
- 将值或条件集与所需类型匹配的任何其他机制
- 每次添加新的IImageFileFormat时,不需要在其他任何地方进行任何更改(也不必使用反射)
- 如果进一步抽象ImageLoader,就可以在单元测试中模拟它,从而使测试每个IImageFileFormat的具体实现变得更加容易
的对象以及一些其他参数,DI系统就会向您返回一个满足您条件的对象
这与IoC(控制反转)一起出现,在IoC中,提供的对象可能需要其他东西,因此在对象返回给用户之前,其他东西会自动创建并安装到对象中(由DI创建)。在vb.net中,如果所有图像加载程序都在同一个程序集中,可以使用分部类和事件来实现所需的效果(有一个类,其目的是在图像加载程序应该注册自己时触发事件;包含图像加载程序的每个文件都可以使用“分部类”向该类添加另一个事件处理程序);C#与vb.net的WithEvents语法没有直接的等价物,但我怀疑部分类是实现相同功能的有限机制。我认为这种方法的唯一困难在于它意味着重新部署代码,而不是以插件方式添加额外的模块。诚然,重新部署并不总是糟糕或困难的——但对于一般情况,如果可能的话,我宁愿避免。我不确定我是否理解你的答案。文件格式首先是如何创建的?创建ImageFileFormat的那个东西,它怎么知道它存在?很好!然而,它需要在不同的地方写东西(