Quel serait le moyen le plus efficace d'instancer un objet selon un type générique transmis à une classe d'usine, par exemple: Comment le feriez-vous? Quelle déclaration de ramification em>? etc ... p> p>
8 Réponses :
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. P>
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
Oui, en effet mais en C # 2.0, je ne peux pas l'initialiser statiquement. Dictionnaire statique privé
Initialiser dans le constructeur peut-être alors?
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. P>
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
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 . P>
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. :)
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! }
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.
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>(); } }
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.
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)](); } }
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 ... p>
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. P>
Je pense qu'il est préférable de le garder simple, peut-être quelque chose comme ceci: Vous venez d'appeler le Utilisation: p> Remarque: chaque addloggerprovider code> 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
code>, mais la création d'un enregistreur ne sera probablement pas un goulard. J'espère que ça va bien. P>
Ilogger
nouvelle () code> générique dans la méthode Ajouter. P> P>
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" :)