1
votes

C # créer une nouvelle instance avec ctor args en utilisant un Dictionary et Func

Je me demandais s'il était possible de créer une nouvelle instance d'un type étant donné une sorte de résolveur en utilisant un dictionnaire, où le nouveau type a des arguments de constructeur. Essentiellement une méthode d'usine.

J'ai quelque chose qui fonctionne, même si j'espérais une façon plus propre de le faire. J'ai rencontré le problème en Java et j'ai pensé que c'était facile en C # - peut-être pas!

Donc, il est basé sur un dictionnaire donné:

Dictionary<Type, Func<ToResolve, Resolved>>

Qui a un résolveur Func<ToResolve, Resolved> pour un type donné. Je veux mapper de ToResolve pour résoudre le passage des champs ToResolve aux paramètres du constructeur ToResolve. ToResolve and Resolve est une classe abstraite pour le scénario. ToResolve in, ToResolve out.

Le scénario de travail est donc:

public abstract class Resolved { }

public class Resolved1 : Resolved
{
    public readonly int x;

    public Resolved1(int x) => this.x = x;
}

public class Resolved2 : Resolved
{
    public readonly string x;

    public Resolved2(string x) => this.x = x;
}

public abstract class ToResolve { }

public class ToResolve1 : ToResolve
{
    public readonly int x;

    public ToResolve1(int x) => this.x = x;
}

public class ToResolve2 : ToResolve
{
    public readonly string x;

    public ToResolve2(string x) => this.x = x;
}

Et cela peut être appelé comme suit:

var toResolve1 = new ToResolve1(100);
var resolved1 = map[toResolve1.GetType()];

var toResolve2 = new ToResolve2("some string");
var resolved2 = map[toResolve2.GetType()];

Avec les déclarations de classe simples comme:

Dictionary<Type, Func<ToResolve, Resolved>> map = new Dictionary<Type, Func<ToResolve, Resolved>>
{
    {
        typeof(ToResolve1), r =>
        {
            var tr = (ToResolve1) r;
            return new Resolved1(tr.x);
        }
    },
    {
        typeof(ToResolve2), r =>
        {
            var tr = (ToResolve2) r;
            return new Resolved2(tr.x);
        }
    }
};

Existe-t-il une manière plus concise de procéder? Idéalement sans avoir à envelopper le lambda sur quelques lignes et en utilisant le casting explicite.

Et ne pas utiliser AutoMapper .


7 commentaires

Vous pouvez utiliser les frameworks IoC pour cela: Ninject, DryIoc, StructureMap, etc. En gros, vous créez un localisateur de service, enregistrez des résolveurs, puis résolvez-les en appelant une méthode unique.


Quelle est votre raison de vouloir faire cela, par opposition à l'utilisation d'un framework IoC comme @eocron mentionné? Est-ce pour votre propre édification, ou essayez-vous d'écrire la DI d'un pauvre parce que vous n'êtes pas au courant de ces cadres?


Lorsque les types sont lus via un fichier de configuration. Croyez-moi, j'en connais beaucoup sur l'IoC. Et ils sont utilisés et abusés dans toute l'industrie. Ils ont leur place à coup sûr. Ici, j'utilise poormans DI pour résoudre un problème de code.


Votre exemple de code semble incomplet, car le type de résolu1 est func et toResolve1 n'est pas du tout utilisé (sauf GetType)


Quel est le problème avec l'approche DI des pauvres? Performance? Écrivez votre propre. Il y en a beaucoup et je suis sûr qu'ils sont maltraités comme vous le dites, mais alors nous pouvons dire que les nombres entiers abusent aussi. Parce que tout le monde les utilise.


@eocron - Ce qui précède est l'approche du pauvre DI (Pure DI), non? Les entiers sont également maltraités - mais peut-être que certains n'apprécient pas les aspects plus riches de la modélisation de domaine de la programmation :) Tout ce que je demandais, c'était comment résoudre un problème de programmation ... dans le code.


@Evk - oui. Ceci est complet pour la forme minimaliste de question sur Stack Overflow. Le code se termine une fois le type résolu.


3 Réponses :


1
votes

On dirait que vous avez juste besoin d'un tas de méthodes de surcharge, vous n'êtes pas sûr que ce soit une manière concise que vous souhaitez?

public static class Resolver
{
    public static Resolved1 Resolve(ToResolve1 r) => new Resolved1(r.x);
    public static Resolved2 Resolve(ToResolve2 r) => new Resolved2(r.x);
}

var resolved1 = Resolver.Resolve(new ToResolve1(100));
var resolved2 = Resolver.Resolve(new ToResolve2("some string"));


0 commentaires

1
votes

Un dictionnaire ne prend pas en charge les valeurs génériques, cependant, vous pouvez écrire votre propre dictionnaire personnalisé:

var dictionary = new ResolverDictionary()
    .Add((ToResolve1 r) => new Resolved1(r.x))
    .Add((ToResolve2 r) => new Resolved2(r.x));
    
var resolver1 = dictionary.Get<ToResolve1>();
var resolved1 = resolver1(new ToResolve1(100));

Qui peut être utilisé comme suit:

class ResolverDictionary
{
    static class Resolver<T> where T : ToResolve
    {
        public static Func<T, Resolved> Instance;
    }
    
    public ResolverDictionary Add<T>(Func<T, Resolved> resolver) where T : ToResolve
    {
        Resolver<T>.Instance = resolver;
        return this;
    }
    
    public Func<T, Resolved> Get<T>() where T : ToResolve
    {
        return Resolver<T>.Instance;
    }
}


1 commentaires

Bonne réponse merci. Il lie le type avec un dictionnaire, qui fonctionnerait bien comme une fabrique abstraite. Ce qui répond totalement à la question.



1
votes

Je pense que vous pouvez faire ce que vous voulez en laissant votre dictionnaire tel quel, mais en ajoutant une méthode distincte pour y ajouter des entrées, comme ceci:

static class Resolver {
    private static readonly Dictionary<Type, Func<ToResolve, Resolved>> _map = new Dictionary<Type, Func<ToResolve, Resolved>>();

    static Resolver() {
        Add((ToResolve1 tr) => new Resolved1(tr.x));
        Add((ToResolve2 tr) => new Resolved2(tr.x));
    }

    private static void Add<TToResolve, TResolved>(Func<TToResolve, TResolved> func) where TToResolve : ToResolve where TResolved : Resolved {
        _map[typeof(TToResolve)] = x => func((TToResolve) x);
    }

    // the only public interface, non-generic
    public static Resolved Resolve(ToResolve x) {
        return _map[x.GetType()](x);
    }
}


1 commentaires

Bonne réponse aussi. J'étais déchiré entre cela et la réponse de Jonathan. Cela encapsule les types.