简体   繁体   English

这个工厂 function 如何自动解析“工厂” object?

[英]How does this factory function manage to resolve the "Factory" object automatically?

So I'm currently looking at some code and I'm trying to figure out how this line manages to resolve the "factory" which apparently is nothing but a delegate that takes a "Type" and returns an object. It's hard for me to form this question because I don't fully understand what's going on.所以我目前正在查看一些代码,我试图弄清楚这条线是如何设法解决“工厂”的,这显然只是一个接受“类型”并返回 object 的委托。我很难形成这个问题是因为我不完全了解发生了什么。 Could someone break it down?有人可以分解吗?

It all starts in the App.cs这一切都始于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();
}

This essentially builds the singleton instance that we're registering这实际上构建了我们正在注册的 singleton 实例

    _services.AddSingleton<INavigationService, NavService>(sp =>
    {
        return new NavService(type => sp.GetRequiredService(type));
    });

And we're passing in whatever sp.GetRequiredService(type) returns as a parameter to the NavService constructor.我们将sp.GetRequiredService(type)作为参数返回给NavService构造函数。 Which seems to be a Func<Type, object> ?哪个似乎是Func<Type, object> Why?为什么? And what is type that we're using when we're using the lambda statement type => sp.GetRequiredService(type)当我们使用 lambda 语句type => sp.GetRequiredService(type)时,我们使用的是什么type

How do we resolve a Func<Type, object> from type ?我们如何从type解析Func<Type, object>

Inside the NavService we're utilizing that delegate by invoking it with a type, which I believe resolves the singleton instance of whatever type we're using when calling NavigateTo<T>NavService内部,我们通过使用一个类型调用它来利用该委托,我相信它解析了我们在调用NavigateTo<T>时使用的任何类型的 singleton 实例

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;
    }
}

So my best guess is that, what we pass in through the constructor is the actual "factory" behind the Microsoft.Extensions.DependencyInjection package that I'm using which is responsible for newing up instances of dependencies that we're registering.所以我最好的猜测是,我们通过构造函数传入的是我正在使用的Microsoft.Extensions.DependencyInjection package 背后的实际“工厂”,它负责更新我们正在注册的依赖项实例。 But that still doesn't answer my question of how we resolve a Func<Type, object> from type which is just of type Type ?但这仍然没有回答我的问题,即我们如何从typeType的类型中解析Func<Type, object>

You are creating an inline lambda as delegate.您正在创建一个内联 lambda 作为委托。 The delegate is the used as a factory to create instances dynamically.委托用作动态创建实例的工厂。

When you have the following constructor...当您具有以下构造函数时...

class NavigationService : INavigationService
{
  public NavigatioService(Func<Type, object> factory) 
  {}
}

...then you must register a related factory delegate in order to be able to construct it with a DI container. ...那么您必须注册一个相关的工厂委托,以便能够使用 DI 容器构造它。 The factory delegate in the above example takes a parameter of type Type and returns an instance of type object .上例中的工厂委托接受类型为Type的参数并返回类型为object的实例。

The factory delegate translates to the following lambda expression: factory委托转换为以下 lambda 表达式:

typeParameter => factory_implementation_that_returns_object;

Instead of instantiating the type manually you can use the service provider to instantiate it for you.您可以使用服务提供商为您实例化它,而不是手动实例化类型。 This will automatically create all required dependencies:这将自动创建所有必需的依赖项:

// Create a delegate that takes a Type as parameter and returns an object using the ServiceProvider
type => sp.GetRequiredService(type)

Now for some reason you have decided to create the NavigationService instance explicitly ( new NavigationService() ).现在出于某种原因,您决定显式创建NavigationService实例 ( new NavigationService() )。 As a drawback you also have to explicitly build all the constructor dependencies, which is the Funcy<Type, object> delegate:作为一个缺点,您还必须显式构建所有构造函数依赖项,即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));
});

Instead of registering the Func<Type, object> as a regular dependency, you decided to create this delegate manually inline.您决定手动内联创建此委托,而不是将Func<Type, object>注册为常规依赖项。 This is not a good decision.这不是一个好的决定。 You always want he IoC container to wire up the dependencies for you.您总是希望 IoC 容器为您连接依赖项。

I don't recommend to do it this way (inline).我不建议这样做(内联)。 It only works when the constructor has a single parameter and this parameter doesn't requests its own dependencies.它仅在构造函数具有单个参数并且此参数不请求其自身的依赖项时有效。 Otherwise you would have to construct all constructor dependencies manually (inline) - which is definitely not what you want.否则,您将不得不手动(内联)构建所有构造函数依赖项——这绝对不是您想要的。

Additionally, it's also better (more robust) to return an explicit type instance from the factory instead of object .此外,从工厂返回显式类型实例而不是object也更好(更健壮)。
Because you have defined the Navigate method as generic with a type constraint that T must be of type ViewModel ...因为您已将Navigate方法定义为具有T必须是ViewModel类型的类型约束的泛型 ...

// The type of T is used as the parameter for the factory delegate
public void NavigateTo<T>() where T : ViewModel

...you should use ViewModel as the explicit return type of your factory. ...您应该使用ViewModel作为工厂的显式返回类型。

To improve your code, the goal is to let the IoC container do all the work.要改进您的代码,目标是让 IoC 容器完成所有工作。 That's what it is for.这就是它的用途。 As a bonus, the code becomes simpler and the configuration feels more intuitive.作为奖励,代码变得更简单,配置感觉更直观。
The key is to design your code that you don't have to create the NavService instance manually.关键是设计您不必手动创建NavService实例的代码。 For this reason you should try to use a parameterless factory delegate.出于这个原因,您应该尝试使用无参数的工厂委托。 See this answer (solution #2) to get an example on how to design the Navigate method.请参阅此答案(解决方案 #2)以获取有关如何设计Navigate方法的示例。 There are good design reasons to not allow every page to navigate to any page.有充分的设计理由不允许每个页面导航到任何页面。 The idea is to let the caller pass in the reference instead of the Type (and instantiate this Type in the NavService ).这个想法是让调用者传入引用而不是Type (并在NavService中实例化此Type )。 Just do it like the Frame does it.就像Frame那样做。

However, to fix and improve your example you simply have to register the factory delegate as a service.但是,要修复和改进您的示例,您只需将工厂委托注册为服务即可。 This way you don't have to create it inline and you don't have to create the receiving type manually too:这样您就不必内联创建它,也不必手动创建接收类型:

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();
}

And we're passing in whatever sp.GetRequiredService(type) returns as a parameter to the NavService constructor.我们将 sp.GetRequiredService(type) 作为参数返回给 NavService 构造函数。

No you aren't.不,你不是。

You are passing in a lambda type => sp.GetRequiredService(type) to the constructor.您正在将 lambda type => sp.GetRequiredService(type)传递给构造函数。 This lambda is the factory method.这个lambda就是工厂方法。

The do.net runtime doesn't have any direct support for lambda's. do.net 运行时不直接支持 lambda。 Instead the C# compiler will translate your code into IL that is equivalent to;相反,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));
    });

So when NavService executes factory.Invoke , the generated method Captures.Factory will be called, which will in turn call the extension method sp.GetRequiredService to obtain the instance of the requested Type .所以当NavService执行factory.Invoke时,会调用生成的方法sp.GetRequiredService Captures.Factory获取请求的Type的实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM