Registering simple types in a specific example

2019-07-29 11:57发布

问题:

Consider the following

ClassA has a constructor that takes an instance of MasterClass and a string and exposes a property called Names of type string[].

ClassB has a constructor that takes an IJuicePresser and a IEnumerable<string>.

ClassC has a constructor that takes an IEnumerable<string>.

Manually I would do something like this to tie them together.

var masterClass = new MasterClass();
var juicePresser = JuicePresser.Create("default");
var classA = new ClassA(masterClass, "string");
var names = classA.Names;
var classB = new ClassB(juicePresser, names as IEnumerable<string>);
var classC = new ClassC(Array.Reverse(names));

How can I set up DryIoc to handle these registrations/resolutions for me?

回答1:

It might be better to move all these run-time names from constructors to corresponding methods. But here is the matching DryIoc setup as-is:

Live on .NET Fiddle

using System;
using System.Collections.Generic;
using System.Linq;
using DryIoc;

public class Program
{
    public static void Main()
    {
        var c = new Container();

        c.Register<MasterClass>();

        c.Register<JuicePresser>(Made.Of(() => JuicePresser.Create("default")));

        // an example how to inject a primitive value: "string" in this case 
        c.Register<ClassA>(made: Parameters.Of.Type<string>(_ => "string"));

        // service key is optional, just to distinguish the list of strings for consumer.
        c.Register<string[]>(
            Made.Of(_ => ServiceInfo.Of<ClassA>(), factory => factory.Names),
            serviceKey: "names"); 

        // register reverse names using ReverseHelper method
        c.Register<string[]>(
            Made.Of(() => ReverseHelper(Arg.Of<string[]>("names"))),
            serviceKey: "reverse-names"); 

        // specify the names and required type (string[]) for injection
        c.Register<ClassB>(made: Parameters.Of.Type<IEnumerable<string>>(typeof(string[]), serviceKey: "names"));

        // specify reverse names for injection
        c.Register<ClassC>(made: Parameters.Of.Type<string[]>(serviceKey: "reverse-names"));

        var classB = c.Resolve<ClassB>();
        var classC = c.Resolve<ClassC>();


        Console.WriteLine(string.Join(" - ", classB.Names.ToArray()));
        // outputs: a - string - z

        Console.WriteLine(string.Join(" - ", classC.Names));
        // outputs: z - string - a
    }

    public static T[] ReverseHelper<T>(T[] target) {
        Array.Reverse(target);
        return target;
    }

    public class MasterClass {}

    public class JuicePresser 
    {
        public readonly string Name;

        private JuicePresser(string name) { Name = name; }

        public static JuicePresser Create(string name) 
        {
            return new JuicePresser(name);
        }
    }

    public class ClassA 
    {
        public readonly string[] Names;

        public ClassA(MasterClass master, string name) {
            Names = new[] { "a", name, "z" }; // for example
        }
    }

    public class ClassB 
    {
        public readonly JuicePresser Presser;   
        public readonly IEnumerable<string> Names;

        public ClassB(JuicePresser presser, IEnumerable<string> names) {
            Presser = presser;
            Names = names;
        }
    }

    public class ClassC
    {

        public readonly string[] Names;

        public ClassC(string[] names) {
            Names = names;
        }
    }
}

Update:

Explaining a bit the part with:

c.Register<string[]>(
    Made.Of(_ => ServiceInfo.Of<ClassA>(), factory => factory.Names),
    serviceKey: "names"); 

DryIoc has support for using not only constructors for service creation, but also static and instance methods (factory methods), properties and fields. Here is the wiki topic.

In example above we use Names property of ClassA object as factory method to register service type "string[]" with service key "names".

Let's see in detail:

// Registering service of ClassA. It means that it can be resolved / injected directly,
// or/and we can use it as a factory for resolving further services
c.Register<ClassA>(made: Parameters.Of.Type<string>(_ => "string"));

// Registering service of type "string[]"
c.Register<string[]>(
    // Made.Of enables specifying factory method to use for "string[]" resolution, instead default constructor selection rules.
    Made.Of(
        // 1) Specifying what factory object should be used,
        // Here we say to use ClassA service registered in container 
        requestNotUsedHere => ServiceInfo.Of<ClassA>(), 

        // 2) Specify that Names property of resolved ClassA object
        // should be used for "string[]" resolution
        classA => classA.Names),

    // As the "string[]" is not very distinctive (unique) service type 
    // (you might register other "string[]" with different meaning), 
    // we identify the names with "names" service key. So when injected or
    // resolved, you need to specify the service key in addition to type.
    serviceKey: "names");

If you want to register with static factory method, property, field, then you don't need to specify request => ServiceInfo.Of<TFactory>() part. BTW, request parameter can be used for conditional selection of factory.



标签: dryioc