Что такое DependencyService и для чего он нужен?

DependencyService в Xamarin.Forms — это встроенный механизм внедрения зависимостей (Dependency Injection), который позволяет приложению на общем уровне (shared code) обращаться к платформенно-специфичным API Android, iOS и других целевых платформ, не нарушая принципов кроссплатформенной архитектуры.

Это ключевой инструмент, когда требуется использовать возможности конкретной платформы, которые не реализованы в самом Xamarin.Forms (например, доступ к файловой системе, сенсорам, Bluetooth, уведомлениям, журналам вызовов, фоновой работе и т.д.).

Основная идея DependencyService

  • На уровне общего кода (shared project) задаётся интерфейс.

  • В каждом платформенном проекте (iOS/Android/Windows) создаётся реализация интерфейса.

  • Эти реализации регистрируются и вызываются с помощью DependencyService.Get<T>().

Таким образом, достигается инверсия зависимостей: общий код зависит не от конкретной реализации, а от интерфейса, который затем разрешается во время выполнения.

1. Зачем нужен DependencyService

Xamarin.Forms предоставляет обёртки над стандартными элементами UI, но не покрывает все возможности платформ Android и iOS. С помощью DependencyService можно:

  • Доступ к датчику GPS, акселерометру, камере.

  • Взаимодействие с нативными API (например, Android NotificationManager).

  • Открытие файлов, работа с Bluetooth, NFC.

  • Доступ к уникальным идентификаторам устройства.

  • Настройка звука, фона, яркости.

  • Использование нативных библиотек сторонних производителей.

2. Основные компоненты DependencyService

  1. Интерфейс, определённый в общем проекте.

  2. Классы-реализации для каждой платформы (с аннотацией [assembly: Dependency]).

  3. Вызов через DependencyService.Get<IService>() в общем коде.

3. Полный пример использования DependencyService

Шаг 1: Создание интерфейса в shared проекте

public interface IDeviceService
{
string GetDeviceName();
}

Шаг 2: Реализация интерфейса в Android проекте

using Android.OS;
using Xamarin.Forms;
using YourAppName.Droid;
\[assembly: Dependency(typeof(DeviceService_Android))\]
namespace YourAppName.Droid
{
public class DeviceService_Android : IDeviceService
{
public string GetDeviceName()
{
return Build.Model;
}
}
}

Шаг 3: Реализация интерфейса в iOS проекте

using UIKit;
using Xamarin.Forms;
using YourAppName.iOS;
\[assembly: Dependency(typeof(DeviceService_iOS))\]
namespace YourAppName.iOS
{
public class DeviceService_iOS : IDeviceService
{
public string GetDeviceName()
{
return UIDevice.CurrentDevice.Name;
}
}
}

Шаг 4: Вызов сервиса в общем коде (например, ViewModel или Page)

var deviceName = DependencyService.Get<IDeviceService>().GetDeviceName();

await DisplayAlert("Устройство", deviceName, "OK");

4. Особенности реализации

  • Классы реализации должны быть public и иметь конструктор без параметров.

  • [assembly: Dependency(typeof(...))] обязателен для регистрации.

  • Вызов DependencyService.Get<T>() возвращает null, если реализация не найдена.

5. Альтернативы DependencyService

Хотя DependencyService является встроенным и простым решением, он имеет ограничения:

  • Работает через рефлексию, что снижает производительность.

  • Не поддерживает внедрение зависимостей с параметрами.

  • Не поддерживает жизненные циклы объектов (singleton, transient и т.д.).

  • Трудно покрывать юнит-тестами.

Поэтому в более сложных проектах используют сторонние DI-фреймворки:

  • Microsoft.Extensions.DependencyInjection (официальная библиотека .NET).

  • **Autofac
    **

  • **Unity
    **
  • **DryIoc
    **

6. Использование в асинхронных сценариях

DependencyService работает только с синхронными интерфейсами. Он не поддерживает внедрение интерфейсов с async-методами напрямую. Чтобы обойти это ограничение, методы возвращают Task, но вызов через DependencyService остаётся синхронным:

public interface IFileService
{
Task&lt;string&gt; ReadFileAsync(string filename);
}

В платформенной реализации:

public async Task&lt;string&gt; ReadFileAsync(string filename)
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), filename);
return await File.ReadAllTextAsync(path);
}

7. Пример: Вибрация устройства

Интерфейс:

public interface IVibrationService
{
void Vibrate();
}

Android-реализация:

using Android.Content;
using Android.OS;
using Xamarin.Forms;
using YourAppName.Droid;
\[assembly: Dependency(typeof(VibrationService))\]
namespace YourAppName.Droid
{
public class VibrationService : IVibrationService
{
public void Vibrate()
{
var vibrator = (Vibrator)Android.App.Application.Context.GetSystemService(Context.VibratorService);
vibrator.Vibrate(VibrationEffect.CreateOneShot(500, VibrationEffect.DefaultAmplitude));
}
}
}

iOS-реализация:

using AudioToolbox;
using Xamarin.Forms;
using YourAppName.iOS;
\[assembly: Dependency(typeof(VibrationService))\]
namespace YourAppName.iOS
{
public class VibrationService : IVibrationService
{
public void Vibrate()
{
SystemSound.Vibrate.PlaySystemSound();
}
}
}  

Вызов в общем коде:

DependencyService.Get&lt;IVibrationService&gt;()?.Vibrate();

8. Работа с несколькими реализациями одного интерфейса

DependencyService поддерживает только одну реализацию интерфейса на платформу. Если требуется больше гибкости, нужно использовать вручную регистрацию или DI-контейнеры.

9. Совместимость с .NET MAUI

В Xamarin.Forms, DependencyService — основной механизм внедрения зависимостей. Однако в .NET MAUI (его преемнике) рекомендован встроенный сервисный контейнер (Microsoft.Extensions.DependencyInjection), который более мощный, типизированный и гибкий. Тем не менее, подход через DependencyService продолжает работать и может использоваться при переносе проектов.

10. Советы по использованию

  • Интерфейсы лучше держать в отдельных папках (Interfaces или Services).

  • Не используйте DependencyService для бизнес-логики — только для платформенной.

  • Оборачивайте вызовы DependencyService.Get<T>() в отдельные классы или property-инъекции — это облегчает тестирование и переход на полноценный DI в будущем.

  • Проверяйте null, особенно если реализация интерфейса не найдена (на одной из платформ).

DependencyService — это простой и эффективный способ интеграции платформенно-специфичных возможностей в Xamarin-приложении при сохранении кроссплатформенной архитектуры. Он даёт разработчику доступ к мощным API Android и iOS из общего кода, используя чистые абстракции и минимальный шаблон кода.