2009-07-17 9 views
7

एक सामान्य प्रकार, एक फैक्टरी वर्ग के लिए पारित उदाहरण के लिए के अनुसार एक वस्तु instanciate का सबसे कारगर तरीका क्या होगा:फैक्टरी बनाना वस्तुओं के लिए एक सामान्य प्रकार सी # अनुसार

public class LoggerFactory 
{ 
    public static ILogger<T> Create<T>() 
    { 
     // Switch Statement? 
     // Generic Dictionary? 
     // EX.: if "T" is of type "string": return (ILogger<T>)new StringLogger(); 
    } 
} 

आप कैसे करते हैं? कौन सा ब्रांचिंग स्टेटमेंट? आदि ...

उत्तर

18

मुझे लगता है कि यह आसान रखने के लिए, शायद कुछ इस तरह सबसे अच्छा है:

public static class LoggerFactory 
{ 
    static readonly Dictionary<Type, Type> loggers = new Dictionary<Type, Type>(); 

    public static void AddLoggerProvider<T, TLogger>() where TLogger : ILogger<T>, new() 
    { 
     loggers.Add(typeof(T), typeof(TLogger)); 
    } 

    public static ILogger<T> CreateLogger<T>() 
    { 
     //implement some error checking here 
     Type tLogger = loggers[typeof(T)]; 

     ILogger<T> logger = (ILogger<T>) Activator.CreateInstance(tLogger); 

     return logger; 
    } 
} 

तुम बस प्रत्येक प्रकार आप का समर्थन करने के क्रम में बढ़ाया जा सकता है चाहते हैं के लिए AddLoggerProvider कहते हैं, यह आप निश्चित रूप से एक जोड़ने सुनिश्चित करता है लाइब्रेरी में इंटरफ़ेस का कार्यान्वयन और कुछ ऑब्जेक्ट नहीं, Activator की वजह से बहुत तेज़ नहीं है, लेकिन लॉगर बनाने की संभावना किसी भी तरह की बाधा नहीं होगी। उम्मीद है कि यह ठीक दिखता है।

उपयोग:

// initialize somewhere 
LoggerFactory.AddLoggerProvider<String, StringLogger>(); 
LoggerFactory.AddLoggerProvider<Exception, ExceptionLogger>(); 
// etc.. 

ILogger<string> stringLogger = LoggerFactory.CreateLogger<string>(); 

नोट: प्रत्येक ILogger<T>Activator के लिए एक parameterless निर्माता आवश्यकता है, लेकिन वह भी ऐड विधि में new() सामान्य बाधा के साथ सुनिश्चित किया जाता है।

+1

के लिए +1 वास्तव में, आप सक्रियकर्ता का उपयोग कर पाएंगे बहुत धीमा है: http://www.csharp-architect.com/post/2009/06/11/Generic- new-T%28%29- प्रदर्शन-अंक और एक समाधान।एएसपीएक्स –

+1

मैंने लिखा "एक्टिवेटर की वजह से बहुत तेज़ नहीं है" ताकि यह "बहुत धीमा" के साथ अनन्य न हो :) –

2

इस बात पर निर्भर करता है कि आप कितने प्रकार के हैंडल करना चाहते हैं। यदि यह छोटा है (10 से कम) मैं एक स्विच स्टेटमेंट का सुझाव दूंगा, क्योंकि यह पढ़ने के लिए तेज़ और क्लीनर होगा। यदि आप और चाहते हैं तो आप एक लुकअप टेबल (हैश मैप, डिक्शनरी, आदि), या कुछ प्रतिबिंब आधारित सिस्टम चाहते हैं।

+0

यह शायद अंततः 10 से ऊपर बढ़ेगा, लेकिन यदि आपके पास कोई लुकअप टेबल उदाहरण दिलचस्प होगा तो दिलचस्प होगा। मैं स्ट्रिंग के रूप में प्रकार की तुलना करने के बारे में बहुत उत्साहित नहीं हूँ। – maxbeaudoin

+0

डिक्टोनरी आपकी आवश्यकताओं के अनुरूप हो सकता है। –

+0

हां, वास्तव में लेकिन सी # 2.0 में मैं इसे स्थिर रूप से प्रारंभ नहीं कर सकता। निजी स्थिर शब्दकोश loggerTable = नए Dictionnary () {{स्ट्रिंग, StringLogger}} – maxbeaudoin

1

स्विच कथन बनाम शब्दकोश - परफॉर्मेंस के लिए कोई फर्क नहीं पड़ता, क्योंकि एक स्विच को एक शब्दकोश में संकलित किया जाता है। तो वास्तव में यह readabilty और लचीलापन का मामला है। स्विच पढ़ने के लिए आसान है, दूसरी तरफ एक शब्दकोश को रनटाइम पर बढ़ाया जा सकता है।

+0

रनटाइम पर फैक्ट्री के शब्दकोश को विस्तारित करने के फायदे होंगे? – maxbeaudoin

+0

स्विच को एक शब्दकोश में संकलित किया गया है? वह फिश लगता है। आप इस आधार पर किस जानकारी का आधार दे रहे हैं? –

+0

संदर्भ के आधार पर, स्विच को एक निर्देश में संकलित किया जा सकता है। मैंने इसके बारे में एक लेख पढ़ा, और नीचे की रेखा यह थी कि सी # में सीएलआई को डिकंपलिंग करना एक डरावना हो सकता है, क्योंकि यह कई रूपों में आता है, डिक्ट उनमें से एक है। – Dykam

1

आप Unity जैसे एक निर्भरता इंजेक्शन ढांचे का उपयोग करने पर विचार कर सकते हैं। आप इसे सामान्य प्रकार के साथ कॉन्फ़िगर कर सकते हैं कि आपका कारक वापस आएगा और कॉन्फ़िगरेशन में मैपिंग करेगा। Here's an example of that

+0

इसे माना जाता है लेकिन मुझे कोड में एक और सरल समाधान चाहिए। इस समाधान को लाने के लिए +1। – maxbeaudoin

+0

हाँ, समझें, लेकिन केवल उत्तर की पूर्णता के लिए, आप कोड में एकता कंटेनर को कॉन्फ़िगर कर सकते हैं। :) –

0

एचआरएम ... आप वास्तव में दिए गए रनटाइम सिस्टम के समर्थन के आधार पर इसके बारे में थोड़ा अधिक चालाक होने का प्रयास कर सकते हैं। मैं वास्तव में अपने कोड में किसी भी सशर्त बयान से बचने की कोशिश करता हूं, यदि मैं कर सकता हूं, खासकर पॉलिमॉर्फिक और गतिशील रूप से बाध्य कोड में। आपके पास एक सामान्य वर्ग है, तो इसका उपयोग क्यों न करें?

उदाहरण के लिए, जावा में, आप विशेष रूप से स्थिर विधि तुम वहाँ मिल गया है इस तरह कुछ करने के लिए का उपयोग कर सकते हैं:

public class LoggerFactory<T> 
{ 
    public static ILogger<T> CreateLogger(Class<? extends SomeUsefulClass> aClass); 
    { 
     // where getLogger() is a class method SomeUsefulClass and its subclasses 
     // and has a return value of Logger<aClass>. 
     return aClass.getLogger(); 

     // Or perhaps you meant something like the below, which is also valid. 
     // it passes the generic type to the specific class' getLogger() method 
     // for correct instantiation. However, be careful; you don't want to get 
     // in the habit of using generics as variables. There's a reason they're 
     // two different things. 

     // return aClass.getLogger(T); 
    } 
} 

आप इसे इस तरह फोन चाहते हैं:

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

यह किसी भी सशर्त होने से बचाता है और इसके अलावा अधिक लचीला है: किसी भी वर्ग जो उप-वर्ग (या लॉगर इंटरफ़ेस लागू करता है) शायद कुछ यूज़फुल क्लास के सही ढंग से टाइप किए गए लॉगर इंस्टेंस को वापस कर सकता है।

+0

मुझे जेनेरिक के अनुसार सही ढंग से टाइप किए गए लॉगर को वापस करने की आवश्यकता है जो कि किसी भी प्रकार का हो सकता है। उदाहरण के लिए, कारखाने को पारित एक अपवाद एक अपवाद लॉगर वापस करेगा। इसे कहीं मैपिंग की जरूरत है। – maxbeaudoin

4

हालांकि मैं आम तौर पर एक निर्भरता इंजेक्शन ढांचे का उपयोग करने की सिफारिश करता हूं, आप प्रतिबिंब के साथ कुछ लागू कर सकते हैं जो उपलब्ध इलोगर इंटरफ़ेस को लागू करने वाले किसी के लिए उपलब्ध प्रकारों को खोजेगा।

मैं सुझाव दूंगा कि आप सावधानी से विचार करें कि कौन से असेंबली में इन लॉगर कार्यान्वयन होंगे और आप कितने एक्स्टेंसिबल और बुलेट प्रूफ को हल करना चाहते हैं। उपलब्ध असेंबली और प्रकारों में रनटाइम खोज करना सस्ती नहीं है। हालांकि, इस प्रकार के डिज़ाइन में विस्तारशीलता की अनुमति देने का एक आसान तरीका है। यह अप-फ्रंट कॉन्फ़िगरेशन के मुद्दे से भी बचता है - हालांकि इसकी आवश्यकता है कि केवल एक ठोस प्रकार इलोगर <> इंटरफ़ेस का एक विशेष संस्करण लागू करें - अन्यथा आपको हल करने के लिए एक अस्पष्ट स्थिति है।

आप प्रत्येक कॉल पर प्रतिबिंबित करने के खर्च से बचने के लिए कुछ आंतरिक कैशिंग करना चाहते हैं()।

यहां कुछ नमूना कोड है जिसके साथ आप शुरुआत कर सकते हैं।

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

इसके लिए LINQ, C# 3.0 और मैं 2.0 की आवश्यकता है लेकिन मैं आप लोगों पर विचार कर निर्भरता इंजेक्शन ढांचे पर विचार कर सकता हूं। – maxbeaudoin

6

मुझे लगता है कि मैं इसे इस तरह करना चाहते हैं:

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

1) मैं लॉगिंग में डाल दी गई जटिलता पर हमेशा आश्चर्यचकित हूं। हमेशा मुझे ज्यादा पसंद है। अगर log4net opensource है, तो मैं आपको सलाह देता हूं कि आप इसे देखें, वास्तव में, आप इसका भी उपयोग कर सकते हैं ...

2) व्यक्तिगत रूप से, जब भी संभव हो, मैं टाइपिंग से बचने की कोशिश करता हूं - यह जेनेरिक के बिंदु को हरा देता है । बस .ToString() विधि का उपयोग करें और इसके साथ किया जाना चाहिए।

+0

बिंदु # 1 – zvolkov

संबंधित मुद्दे