所以我目前正在查看一些代码,我试图弄清楚这一行如何管理解决"工厂"这显然只是一个委托它带有type并返回一个对象。我很难形成这个问题,因为我不完全理解发生了什么。有人能把它拆开吗?
一切从App.cs
public App()
{
IServiceCollection _services = new ServiceCollection();
_services.AddSingleton<MainViewModel>();
_services.AddSingleton<HomeViewModel>();
/* This is the part I don't understand */
_services.AddSingleton<INavigationService, NavService>(sp =>
{
return new NavService(type => sp.GetRequiredService(type));
});
...
_serviceProvider = _services.BuildServiceProvider();
}
这实际上构建了我们要注册的单例实例
_services.AddSingleton<INavigationService, NavService>(sp =>
{
return new NavService(type => sp.GetRequiredService(type));
});
我们将sp.GetRequiredService(type)
返回的参数传递给NavService
构造函数。哪个看起来是Func<Type, object>
?为什么?当我们使用lambda语句type => sp.GetRequiredService(type)
type
是什么如何从type
中解析Func<Type, object>
?
在NavService
内部,我们通过使用一个类型调用它来利用该委托,我相信这解决了我们在调用NavigateTo<T>
时使用的任何类型的单例实例
public class NavService : ObservableObject, INavigationService
{
private readonly Func<Type, object> factory;
private object _currentView;
public NavService(Func<Type, object> factory)
{
this.factory = factory;
}
public object CurrentView
{
get => _currentView;
private set
{
_currentView = value;
OnPropertyChanged();
}
}
public void NavigateTo<T>() where T : ViewModel
{
object viewModel = factory.Invoke(typeof(T)) ?? throw new InvalidOperationException("Could not locate VM.");
CurrentView = viewModel;
}
}
所以我最好的猜测是,我们通过构造函数传入的是实际的&;factory&;在我使用的Microsoft.Extensions.DependencyInjection
包背后,它负责更新我们正在注册的依赖实例。但这仍然没有回答我的问题,我们如何从type
解决Func<Type, object>
,这只是Type
类型?
您正在创建一个内联lambda作为委托。委托被用作动态创建实例的工厂。
当你有下面的构造函数…
class NavigationService : INavigationService
{
public NavigatioService(Func<Type, object> factory)
{}
}
…那么你必须注册一个相关的工厂委托,以便能够用DI容器构造它。上面例子中的工厂委托接受一个Type
类型的参数,并返回一个object
类型的实例。
factory
委托转换为以下lambda表达式:
typeParameter => factory_implementation_that_returns_object;
您可以使用服务提供者为您实例化它,而不是手动实例化类型。这将自动创建所有必需的依赖项:
// Create a delegate that takes a Type as parameter and returns an object using the ServiceProvider
type => sp.GetRequiredService(type)
现在,出于某种原因,您决定显式地创建NavigationService
实例(new NavigationService()
)。作为一个缺点,您还必须显式构建所有构造函数依赖项,这是Funcy<Type, object>
委托:
// Register the INavigationService and tell the IoC container how to build this type
_services.AddSingleton<INavigationService, NavService>(sp =>
{
// Imagine NavService had more constructor dependencies!
// Imagine even this single dependency had 10 dependencies itself,
// where each has n dependencies, where each has...
// You would find yourself to take care to satisfy all of them explicitly at this point...
return new NavService(type => sp.GetRequiredService(type));
});
您没有将Func<Type, object>
注册为常规依赖项,而是决定以内联方式手动创建此委托。这不是一个好的决定。您总是希望IoC容器为您连接依赖项。
我不建议这样做(内联)。只有当构造函数只有一个参数并且这个参数不请求它自己的依赖项时,它才会起作用。否则,你将不得不手动(内联)构造所有的构造函数依赖——这绝对不是你想要的。
此外,从工厂返回显式类型实例也更好(更健壮),而不是object
.
因为您已经将Navigate
方法定义为泛型,并且T
必须是ViewModel
类型…
// The type of T is used as the parameter for the factory delegate
public void NavigateTo<T>() where T : ViewModel
…你应该使用ViewModel
作为你的工厂的显式返回类型。
为了改进代码,目标是让IoC容器完成所有的工作。这就是它的作用。作为奖励,代码变得更简单,配置感觉更直观。关键是设计你的代码,你不必手动创建NavService
实例。因此,您应该尝试使用无参数工厂委托。请参阅此答案(解决方案#2)以获得有关如何设计Navigate
方法的示例。不允许每个页面都导航到任何页面是有很好的设计理由的。其思想是让调用者传入引用而不是Type
(并在NavService
中实例化这个Type
)。就像Frame
那样做吧。
然而,要修复和改进你的例子,你只需要将工厂委托注册为服务。这样你就不需要创建内联的,也不需要手动创建接收类型:
class NavigationService : INavigationService
{
private Func<Type, ViewModel> Factory { get; }
public ViewModel CurrentView { get; private set; }
// Improve the robustness and use a strongly typed result (of type ViewModel).
// The Type parameter of the delegate is later provided by the caller, which is the Navigate method.
public NavigatioService(Func<Type, ViewModel> viewModelFactory)
{
this.Factory = factory;
}
// The type of generic type parameter 'TViewModel'
// is the parameter for the factory delegate
public void NavigateTo<TViewModel>() where TViewModel : ViewModel
{
ViewModel viewModel = this.Factory.Invoke(typeof(TViewModel));
this.CurrentView = viewModel;
}
}
public App()
{
IServiceCollection _services = new ServiceCollection();
_services.AddSingleton<MainViewModel>()
.AddSingleton<HomeViewModel>()
// Let the IoC contaiiner construct the NavigatioService
// and all its dependencies for you
.AddSingleton<INavigationService, NavigatioService>()
// Register the factory delegate as normal dependency,
// so that the IoC container can resolve it
.AddSingleton<Func<Type, ViewModel>(serviceProvider => viewModelType => serviceProvider.GetRequiredService(viewModelType));
_serviceProvider = _services.BuildServiceProvider();
}
我们将sp.GetRequiredService(type)返回的任何参数作为参数传递给NavService构造函数。
不,你不是。
你正在传递一个lambdatype => sp.GetRequiredService(type)
给构造函数。这个lambda是工厂方法。
dotnet运行时没有对lambda的任何直接支持。c#编译器会将你的代码翻译成IL,相当于;
public class Captures{
private IServiceProvider sp;
public Captures(IServiceProvider sp){
this.sp = sp;
}
public object Factory(Type type){
return sp.GetRequiredService(type);
}
}
_services.AddSingleton<INavigationService, NavService>(sp =>
{
var captures = new Captures(sp);
return new NavService(new Func<Type,object>(captures.Factory));
});
因此,当NavService
执行factory.Invoke
时,将调用生成的方法Captures.Factory
,该方法将调用扩展方法sp.GetRequiredService
来获得请求的Type
的实例。