8
votes

Usine Création d'objets selon un type générique C #

Quel serait le moyen le plus efficace d'instancer un objet selon un type générique transmis à une classe d'usine, par exemple: xxx

Comment le feriez-vous? Quelle déclaration de ramification ? etc ...


0 commentaires

8 Réponses :


2
votes

dépend du nombre de types que vous avez l'intention de gérer. Si c'est petit (moins de 10 ans), je suggère une déclaration de commutation, car il sera rapide et plus propre à lire. Si vous voulez plus, vous voulez une table de recherche (carte de hachage, dictionnaire, etc.) ou un système basé sur une réflexion.


4 commentaires

Cela augmentera probablement au-dessus de 10 ans, mais un exemple de table de recherche serait intéressant si vous en avez. Je ne suis pas trop enthousiaste à propos de la comparaison de types de chaîne.


Dictionnaire pourrait répondre à vos besoins.


Oui, en effet mais en C # 2.0, je ne peux pas l'initialiser statiquement. Dictionnaire statique privé Loglertable = nouveau dictionnaire () {{string, stringlogger}} ne fonctionnera pas


Initialiser dans le constructeur peut-être alors?



1
votes

Déclaration de commutation VS Dictionnaire - Peu importe la performance, car un commutateur est compilé dans un dictionnaire. Donc, c'est vraiment une question de lectures et de flexibilité. Le commutateur est plus facile à lire, d'autre part, un dictionnaire peut être étendu au moment de l'exécution.


5 commentaires

Voulez-vous des avantages d'étendre le dictionnaire d'une usine à l'exécution?


Le commutateur est compilé dans un dictionnaire? Cela semble poisson. Sur quelle information en basez-vous?


Selon le contexte, le commutateur peut être compilé dans une dict. Je lis un article à ce sujet, et l'essentiel était que la décompilation CLI en C # peut être une horreur, car elle se présente sous plusieurs formes, dict en être l'un d'entre eux.


Juda, basé sur la recherche de l'IL dans le réflecteur.


Beaud, seulement si c'est une exigence de la tienne



1
votes

Vous pouvez envisager d'utiliser un cadre d'injection de dépendance ici comme Unity . Vous pouvez le configurer avec les types génériques que votre facteur reviendra et effectuera la cartographie de la configuration. Voici un exemple de ce .


2 commentaires

Considéré, mais je voudrais une solution plus simple, en code. +1 pour apporter cette solution.


Yup, comprendre, mais juste pour l'exhaustivité de la réponse, vous pouvez configurer un conteneur d'unité dans le code. :)



0
votes

HRM ... Vous pouvez réellement essayer d'être un peu plus intelligent à ce sujet, en fonction de ce que le système d'exécution donné est pris en charge. J'essaie en fait d'éviter toute déclaration conditionnelle dans mon code si je peux, en particulier en code polymorphe et lié dynamiquement. Vous avez une classe générique là-bas, alors pourquoi ne pas l'utiliser?

Par exemple, en Java, vous pouvez utiliser notamment l'utilisation de la méthode statique que vous avez utilisée pour faire quelque chose comme ceci: P>

public static void main(String[] args)
{
    Logger = LoggerFactory.createLogger(subclassOfUsefulClass.class);
    // And off you go!
}


1 commentaires

Je dois retourner l'enregistreur correctement typé selon un générique pouvant être de n'importe quel type. Par exemple, une exception transmise à l'usine renvoie une exceptionnelle. Il a besoin de cartographier quelque part.



4
votes

Bien que je recommanderais généralement d'utiliser un cadre d'injection de dépendance, vous pouvez mettre en œuvre quelque chose avec une réflexion qui rechercherait les types disponibles pour un qui implémente l'interface d'iLogger appropriée.

Je suggérerais que vous examiniez attentivement quelles assemblées contiendront ces implémentations d'enregistrement et à quel point vous souhaitez que la solution soit extensible et anti-bullet. Effectuer des recherches d'exécution sur les assemblys et les types disponibles n'est pas peu coûteux. C'est cependant un moyen facile de permettre une extensibilité dans ce type de conception. Il évite également de la question de la configuration initiale - toutefois, il faut que seul un seul type de béton implique une version particulière de l'interface ILogger , sinon il y a une situation ambiguë que vous devez résoudre. P>

Vous pouvez Voulez-vous effectuer une mise en cache interne pour éviter les frais de réflexion sur chaque appel à créer (). p>

Voici quelques échantillons de code que vous pourriez commencer. P>

using System;
using System.Linq;
using System.Reflection;

public interface ILogger<T> { /*... */}

public class IntLogger : ILogger<int> { }

public class StringLogger : ILogger<string> { }

public class DateTimeLogger : ILogger<DateTime> { }

public class LoggerFactory
{
    public static ILogger<T> Create<T>()
    {
        // look within the current assembly for matching implementation
        // this could be extended to search across all loaded assemblies
        // relatively easily - at the expense of performance
        // also, you probably want to cache these results...
        var loggerType = Assembly.GetExecutingAssembly()
                     .GetTypes()
                     // find implementations of ILogger<T> that match on T
                     .Where(t => typeof(ILogger<T>).IsAssignableFrom(t))
                     // throw an exception if more than one handler found,
                     // could be revised to be more friendly, or make a choice
                     // amongst multiple available options...
                     .Single(); 

        /* if you don't have LINQ, and need C# 2.0 compatibility, you can use this:
        Type loggerType;
        Type[] allTypes = Assembly.GetExecutingAssembly().GetTypes();
        foreach( var type in allTypes )
        {
            if( typeof(ILogger<T>).IsAssignableFrom(type) && loggerType == null )
                loggerType = type;
            else
                throw new ApplicationException( "Multiple types handle ILogger<" + typeof(T).Name + ">" );                   
        }

        */

        MethodInfo ctor = loggerType.GetConstructor( Type.EmptyTypes );
        if (ctor != null)
            return ctor.Invoke( null ) as ILogger<T>;

        // couldn't find an implementation
        throw new ArgumentException(
          "No mplementation of ILogger<{0}>" + typeof( T ) );
    }
}

// some very basic tests to validate the approach...
public static class TypeDispatch
{
    public static void Main( string[] args )
    {
        var intLogger      = LoggerFactory.Create<int>();
        var stringLogger   = LoggerFactory.Create<string>();
        var dateTimeLogger = LoggerFactory.Create<DateTime>();
        // no logger for this type; throws exception...
        var notFoundLogger = LoggerFactory.Create<double>(); 
    }
}


1 commentaires

Il faut Linq, C # 3.0 et je suis 2.0 mais je pourrais envisager un cadre d'injection de dépendance compte tenu de vos gars.



6
votes

Je pense que je le ferais comme ceci:

public class LoggerFactory<T>
{
    private static Dictionary<Type, Func<ILogger<T>>> LoggerMap = 
        new Dictionary<Type, Func<ILogger<T>>>
    {
        { typeof(string), 
            () => new StringILogger() as ILogger<T> },
        { typeof(StringWriter), 
            () => new StringWriterILogger() as ILogger<T> }
    };

    public static ILogger<T> CreateLogger()
    {
        return LoggerMap[typeof(T)]();
    }
}


0 commentaires

1
votes

1) Je suis toujours étonné de la complexité des personnes mises dans la journalisation. Il me semble toujours trop de surkill pour moi. Si Log4Net est opensource, je vous recommanderais d'aller regarder cela, en infrit, vous pourriez aussi bien l'utiliser ...

2) Personnellement, j'essaie d'éviter la vérification de type chaque fois que possible - il défait le point de générique. Utilisez simplement la méthode .Tostring () et être effectuée avec elle.


0 commentaires

18
votes

Je pense qu'il est préférable de le garder simple, peut-être quelque chose comme ceci: xxx

Vous venez d'appeler le addloggerprovider pour chaque type que vous souhaitez prendre en charge, peut être Extended au moment de l'exécution, il vous assure que vous ajoutez définitivement une implémentation de l'interface à la bibliothèque et non un objet, n'est pas très rapide en raison de l'activateur , mais la création d'un enregistreur ne sera probablement pas un goulard. J'espère que ça va bien.

Utilisation: xxx

Remarque: chaque Ilogger nécessite un constructeur sans paramètre pour le < Code> Activator , mais cela aussi est assuré avec la contrainte nouvelle () générique dans la méthode Ajouter.


2 commentaires

En fait, vous trouverez que l'utilisation de l'activateur est très lente: csharp-architect.com/post/2009/06/11/...


J'ai écrit "n'est pas très rapide à cause de l'activateur" afin que ce ne soit pas exclusif avec "est très lent" :)