You might be asking yourself at this point in time, how do I switch out different classes such as the ISettings
example? Inversion of Control (IoC) is a design pattern meant to complement dependency injection and solve this problem. The basic principle is that many of the objects created throughout your application are managed and created by a single class. Instead of using the standard C# constructors for your ViewModel
or Model
classes, a service locator or factory class would manage them throughout the application.
There are many different implementations and styles of IoC, so let's implement a simple service locator class to use through the remainder of this book as follows:
public static class ServiceContainer { static readonly Dictionary<Type, Lazy<object>> services =new Dictionary<Type, Lazy<object>>(); public static void Register<T>(Func<T> function) { services[typeof(T)] = new Lazy<object>(() => function()); } public static T Resolve<T>() { return (T)Resolve(typeof(T)); } public static object Resolve(Type type) { Lazy<object> service; if (services.TryGetValue(type, out service) { return service.Value; } throw new Exception("Service not found!"); } }
This class is inspired by the simplicity of XNA/MonoGame's GameServiceContainer
class, and follows the service locator pattern. The main differences are the heavy use of generics and the fact that it is a static class.
To use our ServiceContainer
class, we would declare the version of ISettings
or other interfaces that we want to use throughout our application by calling Register
as seen in the following lines of code:
//iOS version of ISettings ServiceContainer.Register<ISettings>(() => new AppleSettings()); //Android version of ISettings ServiceContainer.Register<ISettings>(() => new DroidSettings()); //You can even register ViewModels ServiceContainer.Register<SettingsViewModel>(() => new SettingsViewModel());
On iOS, you could place this registration code in either your static void Main()
method or in the FinishedLaunching
method of your AppDelegate
class. These methods are always called before the application is started.
On Android, it is a little more complicated. You cannot put this code in the OnCreate
method of your activity, set as the main launcher. In some situations, the OS can close your application but restart it later in another activity. This situation is likely to cause an exception to be raised. The guaranteed safe place to put this is in a custom Android Application
class, which has an OnCreate
method that is called prior to any activities being created in your application. The following lines of code show the use of the Application
class:
[Application] public class Application : Android.App.Application { //This constructor is required public Application(IntPtr javaReference, JniHandleOwnership transfer): base(javaReference, transfer) { } public override void OnCreate() { base.OnCreate(); //IoC Registration here } }
To pull a service out of the ServiceContainer
class, we could rewrite the constructor of the SettingsViewModel
class like the following lines of code:
public SettingsViewModel() { this.settings = ServiceContainer.Resolve<ISettings>(); }
Likewise, you would use the generic Resolve
method to pull out any ViewModel
classes you would need to call from within controllers on iOS or activities on Android. This is a great, simple way to manage dependencies within your application.
There are, of course, some great open source libraries out there that implement IoC for C# applications. You might consider switching to one of them if you need more advanced features for service location, or just want to graduate to a more complicated IoC container.
Here are a few libraries that have been used with Xamarin projects:
3.142.164.120