I've been reading about Unity's dependency injection and I understand it's a thing and that it allows you to type a class to an interface. What I'm curious about is, do I HAVE to? In the below scenario there's a TerrainGenerator and TileCreator in the same space. How can I get the TileCreator within the generator as a dependency?
http://geekswithblogs.net/danielggarcia/archive/2014/01/23/introduction-to-dependency-injection-with-unity.aspx walks me through registering a type, but I read somewhere that as long as the class is visible in the Unity Assets section it'll be able to auto inject it, I just can't figure out the syntax (if it's possible).
Update
I could put all the classes in a single file... with a large system that could be pretty annoying. In the meantime it's an approach I'll try - better than having it not work at all.
update
Seems like Unity should be able to look at a class' constructor and perform these resolutions automatically and inject them in my class' constructor. Is that possible?
If you are looking for DI for the Unity3d engine, maybe this would work (I've not used it, but the feedback is positive) https://github.com/modesttree/Zenject
If you are talking about Microsoft's Unity DI library, you should be able to do this:
container.RegisterTypes(
AllClasses.FromLoadedAssemblies(),
WithMappings.FromMatchingInterface,
WithName.Default);
I always use the following code. When I load an application, the application looks in the directory for all Dlls. This way when you load a class with reflection it searches for the Dlls and exes. You can also add some more paths to search.
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(currentDomain_AssemblyResolve);
Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string defaultFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string assemblyName = new AssemblyName(args.Name).Name;
string assemblyNameDll = assemblyName + ".dll";
string assemblyNameExe = assemblyName + ".exe";
string assemblyPathDll = Path.Combine(defaultFolder, assemblyNameDll);
string assemblyPathExe = Path.Combine(defaultFolder, assemblyNameExe);
string assemblyPathToUse = null;
if (File.Exists(assemblyPathDll))
{
assemblyPathToUse = assemblyPathExe;
}
else if (File.Exists(assemblyPathExe))
{
assemblyPathToUse = assemblyPathExe;
}
else
{
IEnumerable<string> merge = AssemblyFolders.Values;
if (!string.IsNullOrEmpty(TempLoadingFolder))
{
merge = AssemblyFolders.Values.Union(new List<string>() { TempLoadingFolder });
}
foreach (var folder in merge)
{
assemblyPathDll = Path.Combine(folder, assemblyNameDll);
assemblyPathExe = Path.Combine(folder, assemblyNameExe);
if (File.Exists(assemblyPathDll))
{
assemblyPathToUse = assemblyPathDll;
break;
}
else if (File.Exists(assemblyPathExe))
{
assemblyPathToUse = assemblyPathExe;
break;
}
}
}
Assembly assembly = null;
if (assemblyPathToUse != null && File.Exists(assemblyPathToUse))
{
assembly = Assembly.LoadFrom(assemblyPathToUse);
}
return assembly;
}
Don't think it matters if you have the classes in the same file or not. Unity needs to know how to create the instance given the type.
If RegisterInstance is used, the specific object passed as argument is returned everytime Resolve is called for the type. If the type is registered using RegisterType (or not registered at all for concrete classes), Unity will try to instantiate the type by using the constructor with most number of arguments. For each of the parameter types, Unity will try to resolve them recursively.
Registering mappings for interface types to concrete types is mandatory but registering concrete types themselves is optional.
Sample code:
using Microsoft.Practices.Unity;
using System;
namespace Unity
{
interface IFooBar
{
string Message();
}
class Foo
{
string msg;
public Foo()
{
msg = "Hello";
}
public override string ToString()
{
return msg;
}
}
class Bar
{
private Foo _f;
private IFooBar _fb;
public Bar(Foo f, IFooBar fb)
{
this._f = f;
this._fb = fb;
}
public override string ToString()
{
return _f.ToString() + " World " + _fb.Message();
}
}
class FooBar : IFooBar
{
public string Message()
{
return "Unity!";
}
}
class Program
{
static void Main(string[] args)
{
UnityContainer container = new UnityContainer();
container.RegisterType<IFooBar, FooBar>(); // required
container.RegisterType<Foo>(); // optional
container.RegisterType<Bar>(); // optional
var mybar = container.Resolve<Bar>();
Console.WriteLine(mybar);
}
}
}
https://msdn.microsoft.com/en-us/library/microsoft.practices.unity.iunitycontainer_methods(v=pandp.20).aspx
No, you don't have to use interfaces, you can register and resolve concrete types as well.
For example, you can register the TerrainGenerator and TileCreator as follows:
var myTileCreator = new TileCreator();
container.RegisterType<TerrainGenerator>(new PerThreadLifetimeManager(), new InjectionFactory(c => new TerrainGenerator(myTileCreator)));
To resolve TerrainGenerator:
TerrainGenerator generator = container.Resolve<TerrainGenerator>();
To resolve TerrainGenerator with a different TileCreator:
TerrainGenerator generator = container.Resolve<TerrainGenerator>(new ParameterOverride("tileCreator", new TileCreator()));
You may want to read Dependency Injection with Unity - Patterns and Practices for more useful information like properties injection and alike.
Hope that helps.
Related
I'm attempting to unit test some code that use ZipFile.OpenRead within to extract some XML files from a ZIP (Writing the unit tests with moq)
Is there a way I can replace the call to ZipFile.OpenRead with my own result? I have used shims for similar situations, but I can't figure out what to do in this situation and documentation on shims is pretty sparse.
Here is (part of) the method that needs unit-testing:
public IEnumerable<ConfigurationViewModel> ExtractXmlFromZip(string fileName)
{
var configs = new List<ConfigurationViewModel>();
using (var archive = ZipFile.OpenRead(fileName))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.FullName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
LoadConfigfromZipArchiveEntry(entry, configs)
}
}
}
return configs;
}
There isn't a way to mock a static class like ZipFile using mock, you would either wrap it using an IZipFileWrapper (say)
public IZipFileWrapper
{
ZipArchive OpenRead(string fileName)
}
public ZipFileWrapper : IZipFileWrapper
{
public ZipArchive OpenRead(string fileName)
{
return ZipFile.OpenRead(fileName)
}
}
Then the code becomes:
public MyObj
{
private IZipFileWrapper zipFileWrapper;
public MyObj(IZipFileWrapper zipFileWrapper)
{
this.zipFileWrapper = zipFileWrapper;
}
public IEnumerable<ConfigurationViewModel> ExtractXmlFromZip(string fileName)
{
var configs = new List<ConfigurationViewModel>();
// Call the wrapper
using (var archive = this.zipFileWrapper.OpenRead(fileName))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.FullName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
LoadConfigfromZipArchiveEntry(entry, configs)
}
}
}
return configs;
}
}
And a test of
[TestMethod]
public void ExtractXmlFromZip_Test()
{
var myThing = new MyObj();
var fileName = "my.zip";
ZipArchive myZipArchive = CreateTestZipFile(); // Set up your return
var mockWrapper = new Mock<IZipFileWrapper>();
mockWrapper.Setup(m => m.OpenRead(fileName)).Returns(myZipArchive);
var configs = myThing.ExtractXmlFromZip(fileName);
// Assert
}
}
You would probably need to wrap more to get that passing, but hopefully that shows the concept.
(wrote this before I realised it was moq you were asking about and not shims from Microsoft Fakes)
There is an easier way to use Shims that can get to this code - from Microsoft Fakes.
The ZipFile class is part of System.IO.Compression.FileSystem, which is in the dll of the same name.
To let us use a ShimZipFile, we need to add a Fakes Assembly:
Note: We need to Fake System.IO.Compression.FileSystem (i.e. the dll) not System.IO.Compression dlll (which is the namespace of ZipFile).
Which should make the following changes to the project:
Then we can use it in a test such as:
[TestMethod]
public void ExtractXmlFromZip_Test()
{
var myThing = new MyObj();
var fileName = "my.zip";
ZipArchive myZipArchive = CreateTestZipFile(); // Set up your return
using (ShimsContext.Create())
{
System.IO.Compression.Fakes.ShimZipFile.OpenReadString = (filename) => myZipArchive;
var configs = myThing.ExtractXmlFromZip(fileName);
// Assert
}
}
There is info on MSDN about the naming conventions for shims that fakes generates.
One of the ways I have recently taken to is using a private delegated member on a class which has the single purpose of wrapping a concrete implementation.
Fortunately, for me, R# makes it easy to create a concrete proxy class with single private member, expose the public members on the wrapped type (through the Delegate Members code generator), then extract the interface to decouple, and provide ways to mock, and verify during testing.
I work an an automation team designing tests for electronic components. One thing our framework sorely needs is a single source point for our driver objects for the various pieces of test equipment at a workbench (right now, driver object creation is very wild-west).
Basically, the idea would be there would be one object, constructed based on a configuration file(s), which is the single place all other test code looks to to get the driver objects, based on a name string. I'll call it a "DriverSource" here.
The problem is, these drivers do not present similar interfaces at all. One might be a power supply (with methods like "SetVoltage" and "SetCurrentLimit"), while another might be a digital multimeter (with methods like "ReadVoltage" or "ReadCurrent").
The best solution I've come up with is to have a method with the following declaration:
public object GetDriver(string name);
Then, the test code using my "DriverSource" object would call that method, and then cast the System.Object to the correct driver type (or more accurately, the correct driver interface, like IPowerSupply).
I think casting like that is acceptable because whatever test code is about to use this driver had better know what the interface is. But I was hoping to get some input on whether or not this is an anti-pattern waiting to bite me. Any better pattern for solving this issue would also be greatly appreciated.
A final note: I think this is obvious, but performance is essentially a non-issue for this problem. Fetching the drivers is something will happen less than 100 times in a test run that can last hours.
If you already know the type and you're going to cast to an interface or class anyway, a better approach would be to hand the method call a type parameter.
public T GetDriver<T>(string name);
You can then use a Factory pattern to return you an object of the appropriate type from the method.
public T GetDriver<T>(string name)
{
switch(typeof(T).Name)
{
case "Foo":
// Construct and return a Foo object
case "Bar":
// Construct and return a Bar object
case "Baz":
// Construct and return a Baz object
default:
return default(T);
}
}
Usage:
var driver = GetDriver<Foo>(someString); // Returns a Foo object
If you really want to make this generic, I would use a factory pattern.
Lets start off by identifying the type structure:
public interface IDriver
{
}
public interface IPowerSupply : IDriver
{
void SetVoltage();
void SetCurrent();
}
public interface IMultimeter : IDriver
{
double MeasureVoltage();
}
Which you can add to or remove from as needed. Now we need a way for the factory to auto-discover the correct types and provide the configuration information to it. So lets create a custom attribute:
public class DriverHandlerAttribute : Attribute
{
public Type DriverType { get; set; }
public string ConfigurationName { get; set; }
}
And then we need a place to store configuration data. This type can contain whatever you want, like a dictionary of keys/values that are loaded from configuration files:
public class Configuration
{
public string DriverName { get; set; }
public string OtherSetting { get; set; }
}
Finally we can create a driver. Lets create an IPowerSupply:
[DriverHandler(DriverType = typeof(IPowerSupply), ConfigurationName="BaseSupply")]
public class BasePowerSupply : IPowerSupply
{
public BasePowerSupply(Configuration config) { /* ... */ }
public void SetVoltage() { /* ... */ }
public void SetCurrent() { /* ... */ }
}
The important part is that it is decorated with the attribute and that it has a constructor (although I created the factory so that it can use default constructors too):
public static class DriverFactory
{
public static IDriver Create(Configuration config)
{
Type driverType = GetTypeForDriver(config.DriverName);
if (driverType == null) return null;
if (driverType.GetConstructor(new[] { typeof(Configuration) }) != null)
return Activator.CreateInstance(driverType, config) as IDriver;
else
return Activator.CreateInstance(driverType) as IDriver;
}
public static T Create<T>(Configuration config) where T : IDriver
{
return (T)Create(config);
}
private static Type GetTypeForDriver(string driverName)
{
var type = (from t in Assembly.GetExecutingAssembly().GetTypes()
let attrib = t.GetCustomAttribute<DriverHandlerAttribute>()
where attrib != null && attrib.ConfigurationName == driverName
select t).FirstOrDefault();
return type;
}
}
So to use this, you would read in the configuration data (loaded from XML, read from a service, files, etc). You can then create the driver like:
var driver = DriverFactory.Create(configuration);
Or if you are using the generic method and you know the configuration is for a power supply, you can call:
var driver = DriverFactory.Create<IPowerSupply>(configuration);
And when you run your tests, you can verify that you get the right data back, for example, in your test method:
Assert.IsTrue(driver is IPowerSupply);
Assert.IsTrue(driver is BaseSupply);
Assert.DoesWhatever(((IPowerSupply)driver).SetVoltage());
And so-on and so-forth.
I would go with this code:
public T GetDriver<T>(string name)
{
return ((Func<string, T>)_factories[typeof(T)])(name);
}
The _factories object looks like this:
private Dictionary<Type, Delegate> _factories = new Dictionary<Type, Delegate>()
{
{ typeof(Foo), (Delegate)(Func<string, Foo>)(s => new Foo(s)) },
{ typeof(Bar), (Delegate)(Func<string, Bar>)(s => new Bar()) },
{ typeof(Baz), (Delegate)(Func<string, Baz>)(s => new Baz()) },
};
Basically the _factories dictionary contains all of the code to create each object type based on string parameter passed in. Note that in my example above the Foo class takes s as a constructor parameter.
The dictionary can also then be modified at run-time to suite your needs without needing to recompile code.
I would even go one step further. If you define this factory class:
public class Factory
{
private Dictionary<Type, Delegate> _factories = new Dictionary<Type, Delegate>();
public T Build<T>(string name)
{
return ((Func<string, T>)_factories[typeof(T)])(name);
}
public void Define<T>(Func<string, T> create)
{
_factories.Add(typeof(T), create);
}
}
You can then write this code:
var drivers = new Factory();
drivers.Define(s => new Foo(s));
drivers.Define(s => new Bar());
drivers.Define(s => new Baz());
var driver = drivers.Build<Foo>("foo");
I like that even better. It's strongly-typed and easily customized at run-time.
I have an interface like
public interface IAddressProvider
{
string GetAddress(double lat, double long);
}
In my consuming class I want to cycle through the concrete providers until I get a result, like (simplified):
string address;
address = _cachedAddressProvider.GetAddress(lat, long);
if(address == null)
address = _localDbAddressProvider.GetAddress(lat, long);
if(address = null)
address = _externalAddressProvider.GetAddress(lat, long);
return address ?? "no address found";
I can then mock each provider for unit testing, setting null as the return value to appropriately test all code paths.
How would i inject the interface into my consuming class (preferably using StructureMap) so that each concrete implementation is correctly resolved?
The fact that you have multiple address-providers is not something the calling code should have to deal with. So create a specific provider-proxy to handle with these multiple providers.
Like this.
public interface IAddressProvider {
string GetAddress(double lat, double long);
}
public class AddressProviderProxy: IAddressProvider {
public AddressProviderProxy(IAddressProvider[] providers) {
_providers = providers; // TODO: Add a NULL guard
}
private readonly IAddressProvider[] _providers;
string IAddressProvider.GetAddress(double lat, double long) {
foreach (var provider in _providers) {
string address = provider.GetAddress(lat, long);
if (address != null)
return address;
}
return null;
}
}
// Wire up using DI
container.Register<IAddressProvider>(
() => new AddressProviderProxy(
new IAddressProvider[3] {
cachedAddressProvider,
localDbAddressProvider,
externalAddressProvider
}
)
);
// Use it
IAddressProvider provider = ...from the container, injected..
string address = provider.GetAddress(lat, long) ?? "no address found";
Could you not just use container.GetAllInstances?, like so:
var address = new List<string>();
foreach (var provider in container.GetAllInstances<IAddressProvider>())
{
address.add(provider.GetAddress(lat, long));
}
Edit:
I see what you mean now. If you're using StructureMap 2.x then I would recommend looking at the Conditionally clause. However this has been removed in version 3 in favour of creating your own builder class that should be responsible for returning the correct instance.
For example:
public class AddressProviderBuilder : IInstanceBuilder
{
private readonly IContainer container;
public AddressProviderBuilder(IContainer container)
{
this.container = container;
}
public IAddressProvider Build()
{
foreach (var provider in this.container.GetAllInstances<IAddressProvider>())
{
if (provider.GetAddress(lat, long) != null)
{
return provider;
}
}
return null;
}
}
I'm not familiar with StructureMap specifically but there are two solutions as far as I can see.
1) Named Instances - You register your 3 concrete implementations of IAddressProvider with StructureMap as named instances, and then configure the constructor parameters.
StructureMap named instance configuration: http://docs.structuremap.net/InstanceExpression.htm#section14
Using named parameters in constructor injection: http://lookonmyworks.co.uk/2011/10/04/using-named-instances-as-constructor-arguments/
2) More Interfaces - Assuming that there's only ever going to be a few IAddressProvider implementations and not hundreds you could create an ICachedAddressProvider, ILocalDbAddressProvider, and IExternalAddressProvider that implement IAddressProvider and then use those in the constructor of the consuming class.
If there are likely to be significantly more concrete IAddressProvider implementations then you might want to look into something along the lines of an Abstract Factory instead.
I'd like to create an instance of a class using unity where the class has two constructors with the same number of parameters.
Here is the instantiation:
_unityContainer.Resolve<IGradeType>(new ParameterOverride("gradeTypeStringFromXmlFile", gradeTypeStringFromXmlFile));
And here are the constructors:
public GradeType(string gradeTypeStringFromXmlFile)
{
_gradeTypeStringFromXmlFile = gradeTypeStringFromXmlFile;
}
public GradeType(Enum.GradeType gradeType)
{
_gradeType = gradeType;
}
If I try to do this I get an exception saying The type GradeType has multiple constructors of length 1. Unable to disambiguate.
I can set the attribute [InjectionConstructor] over one constructor to make it work with one, but then I can't create an instance with unity using the other constructor.
Is it some way to have multiple constructors with equal number of parameters and still use unity to create the instances?
Yes it's possible to tell Unity which constructor should it use, but you can only do this when you register your type with InjectionConstructor. If you want to use both constructor it's even complicated because you have to name your registrations and use that name when resolving.
Sample built with Unity version 2.1.505:
var continer = new UnityContainer();
continer.RegisterType<IGradeType, GradeType>("stringConstructor",
new InjectionConstructor(typeof(string)));
continer.RegisterType<IGradeType, GradeType>("enumConstructor",
new InjectionConstructor(typeof(EnumGradeType)));
IGradeType stringGradeType = continer.Resolve<IGradeType>("stringContructor" ,
new DependencyOverride(typeof(string), "some string"));
IGradeType enumGradeType = continer.Resolve<IGradeType>("enumConstructor",
new DependencyOverride(typeof(EnumGradeType), EnumGradeType.Value));
An alternative option using Reflection and following the Strategy Pattern.
1) Create a base class for the constructors' arguments
public abstract class ConstructorArgs
{
}
2) Create a sequence of different concrete arguments classes:
public class StringArg : ConstructorArgs
{
public string _gradeTypeStringFromXmlFile { get; set; }
public StringArg (string gradeTypeStringFromXmlFile)
{
this._gradeTypeStringFromXmlFile = gradeTypeStringFromXmlFile ;
}
}
public class EnumArg : ConstructorArgs
{
public Enum.GradeType _gradeType { get; set; }
public EnumArg (Enum.GradeType gradeType)
{
this._gradeType = gradeType ;
}
}
3) Now in your GradeType class create the methods required for the Reflection. The ParseArguments scans the args for properties and for each one that it finds, it copies its value to the respective property of the GradeType using the SetProperty. Since it uses the property name for the matching, it is important to keep the same property name across both the GradeType and the concrete ConstructorArgs:
private void SetProperty(String propertyName, object value)
{
var property = this.GetType().GetProperty(propertyName);
if (property != null)
property.SetValue(this, value);
}
private void ParseArguments(ConstructorArgs args)
{
var properties = args.GetType().GetProperties();
foreach (PropertyInfo propertyInfo in properties)
{
this.SetProperty(propertyInfo.Name,
args.GetType().GetProperty(propertyInfo.Name).GetValue(args));
}
}
4) In your GradeType class create the respective properties (mind that you must use exactly the same names and types that you used in the concrete ConstructorArgs but you can use any access modifiers you like)
public string _gradeTypeStringFromXmlFile { get; set; }
public Enum.GradeType _gradeType { get; set; }
5) Create a constructor for your GradeType class with a parameter of type ConstructorArgs:
public GradeType(ConstructorArgs args)
{
this.ParseArguments(args);
}
6) Now you can register the GradeType in Unity using a single constructor but you can pass in different types as arguments when resolving it:
_unityContainer.RegisterType<IGradeType, GradeType>(
new InjectionConstructor( typeof(ConstructorArgs) ));
var args1 = new StringArg(gradeTypeStringFromXmlFile); // string
IGradeType gradeType1 = _unityContainer.Resolve<IGradeType>(
new ResolverOverride[]{new ParameterOverride("args", args1)});
var args2 = new EnumArg(gradeType); // enum
IGradeType gradeType2 = _unityContainer.Resolve<IGradeType>(
new ResolverOverride[]{new ParameterOverride("args", args2)});
If you are planning to repeatedly resolve your type in an iteration that approach might not be ideal, since Reflection comes with a performance penalty.
Remove one constructor, and cast the string to the enum, or vice-versa, and then resolve using the container.
How can i make the design of this class more dynamic so i can add new extensions and types as required.
public class Processor
{
public Processor(string fileName)
{
string extension = Path.GetExtension(fileName);
if(extension == "jpg" || extension == "gif" || extension == "png")
{
//Process Image file type
}
else if(extension == "xls" || extension == "xlsx")
{
//Process spreadsheet type
}
else if(extension == "doc" || extension == "docx")
{
//Process document file type
}
//and so forth ...
}
}
We may need to process .tiff files in future or we may need to process video files, which means a new if branch
else if(extension == "avi" || extension == "mp4")
{
//Process video file type
}
As you can see this can get very long.
The valid files types and groups are stored in a DB...
Can anyone recommend any patterns or clever ideas to solve this problem?
Cheers
Use a dictionary.
Dictionary<string, IFileHandler> fileHandlers = new Dictionary<string, IFileHandler>
{
{ "jpg", imageHander },
{ "gif", imageHander },
{ "xls", spreadsheetHander },
// ...
};
Then use it as follows:
public void Process(string fileName)
{
string extension = Path.GetExtension(fileName);
// TODO: What should happen if the filetype is unknown?
fileHandlers[extension].Process(fileName);
}
The valid files types and groups are stored in a DB...
Then you probably want to query the database to get the correct group from the extension and use the group as the dictionary key rather than the extension.
I would recommend coming up with an interface for your various file processors to implement:
public interface IFileProcessor
{
void Process(string fileName);
IEnumerable<string> FileExtensions {get;}
}
Then use a factory to get the appropriate processor for a particular file name. Like the other answerers, I'm recommending you use a Dictionary to keep a mapping of the processors and their file names. This example uses reflection to register every file processor that it finds in the loaded assemblies. This fits your extensibility requirement well, because all you need is for some loaded assembly to have a class that implements this interface, and it will automatically be registered with the factory. However, you can come up with whatever system you want for registering these types:
public class ProcessorFactory
{
static IDictionary<string, IFileProcessor> ProcessorsByExtension =
new Dictionary<string, IFileProcessor>();
static ProcessorFactory()
{
var processorTypes =
from a in AppDomain.CurrentDomain.GetAssemblies()
from t in a.GetTypes()
where typeof(IFileProcessor).IsAssignableFrom(t)
select t;
foreach(var t in processorTypes)
{
// Preferably use your DI framework to generate this.
var processor = (IFileProcessor)Activator.CreateInstance(t);
foreach(var ext in processor.FileExtensions)
{
if(ProcessorsByExtension.ContainsKey(ext))
{
throw new InvalidOperationException(
"Multiple processors are registered to extension " + ext);
}
ProcessorsByExtension[ext] = processor;
}
}
}
public IFileProcessor GetProcessorForFile(string fileName)
{
string extension = Path.GetExtension(fileName);
return ProcessorsByExtension[extension];
}
}
A typical implementation of the file processor interface might look like this:
public class ImageFileProcessor : IFileProcessor
{
public IEnumerable<string> FileExtensions
{
get {return new[]{"jpg", "gif", "png"};}
}
public void Process(string fileName)
{
// Process Image file type
}
}
This approach keeps your code modular and extensible, while providing excellent performance even if you have an enormous list of file processors.
This structure is generally a bad thing if it's expected to be extended, for the reasons you state (primarily having to to back in and add code branches).
I would implement this as a Dictionary, keyed to the file extension strings, and containing method delegates as values. That would, if properly designed, allow you to add new extensions without affecting old code, since the new code would be in a completely different method that could even live outside the class that has this logic. Then, simply look up the proper delegate to call by the file extension.
Basic example:
//Put this in your class somewhere
public readonly Dictionary<string, Action<FileInfo>> fileHandlers = new Dictionary<string, Action<FileInfo>>();
...
//depending on the dictionary's visibility, you can add these from pretty much anywhere
fileHandlers.Add("xls", ProcessExcelFile);
fileHandlers.Add("xlsx", ProcessExcelFile);
fileHandlers.Add("jpg", ProcessImageFile);
...
//Then all you have to do to invoke the logic is...
fileHandlers[extension](fileInfo);
One way might be to build a table (list), where each item contains the extension along with information about what to do with names with that extension.
You can then write a simple loop, that compares against each extension in the table.
If what you do with each extension is complex and can't be defined in a simple table, then you could instead include a delegate. When the loop finds the matching extension, it could call the corresponding delegate to process the name.
There is a rule for this refactoring--
Replace condition with Polymorphism/strategy.
The obvious problem with if/switch is very likely to have errors in such code, and it is difficult to maintain or enhance.
Considering the same and the "Open-Closed Principal" ( -- Classes should be open for extension but closed for modifications.).
I would suggest for the code below.
public class Processor
{
private Dictionary<string,FileParserBase> _fileExtension2FileParser;
public Processor() {
_fileExtension2FileParser = new Dictionary<string, FileParserBase>();
AddParser(new DocExtensionWordParser());
AddParser(new DocXExtensionWordParser());
//..more,more
}
private void AddParser(FileParserBase fileParserBase) {
_fileExtension2FileParser.Add(fileParserBase.Extension, fileParserBase);
}
public void Process(string fileName)
{
string extension = Path.GetExtension(fileName);
FileParserBase fileParser;
if (_fileExtension2FileParser.TryGetValue(extension, out fileParser)) {
fileParser.Process(fileName);
}
}
}
public interface FileParserBase
{
string Extension { get; }
void Process(string filePath);
}
public abstract class WordParserBase : FileParserBase
{
private string _extension;
public WordParserBase(string extension)
{
_extension = extension;
}
public override void Process(string filePath)
{
//Do the processing for WORD Document
}
public override string Extension
{
get { return _extension; }
}
}
public class DocExtensionWordParser : WordParserBase
{
public DocExtensionWordParser():base("doc"){}
}
public class DocXExtensionWordParser : WordParserBase
{
public DocXExtensionWordParser() : base("docx") { }
}
for this particular case I'd build something like
List<string> excelExtensions = new List<string>(){ "xls", "xlsx" };
List<string> wordExtensions = new List<string>(){ "doc", "docx" };
if(excelExtensions.Contains(extension))
{
}
if(wordExtensions.Contains(extension))
{
}
etc?