2010-04-21 13 views
21

स्टेटिक सी # में जेनेरिक क्लास के प्रमुख उपयोग क्या हैं? उनका उपयोग कब किया जाना चाहिए? उनके उदाहरणों का सबसे अच्छा उदाहरण क्या है?स्थैतिक जेनेरिक कक्षाओं के लिए उपयोग करता है?

उदा।

public static class Example<T> 
{ 
    public static ... 
} 

चूंकि आप उनमें विस्तार विधियों को परिभाषित नहीं कर सकते हैं, इसलिए वे उनकी उपयोगिता में कुछ सीमित हैं। इस विषय पर वेब संदर्भ स्पष्ट रूप से दुर्लभ हैं कि बहुत से लोग उनका उपयोग नहीं कर रहे हैं। यहां पर कुछ है: -

http://ayende.com/Blog/archive/2005/10/05/StaticGenericClass.aspx

Static Generic Class as Dictionary


जवाब का सारांश को देखते हुए

महत्वपूर्ण मुद्दों होने के लिए "क्या स्थिर साथ एक स्थिर सामान्य वर्ग के बीच का अंतर है दिखाई देते हैं विधियों और स्थैतिक जेनेरिक सदस्यों के साथ गैर-सामान्य स्थैतिक वर्ग? "

किस निर्णय का उपयोग करना है, "क्या कक्षा को आंतरिक रूप से टाइप-विशिष्ट स्थिति को स्टोर करने की आवश्यकता है?"

यदि टाइप-विशिष्ट आंतरिक स्टोरेज की आवश्यकता नहीं है तो जेनेरिक स्थिर विधियों के साथ एक स्थिर गैर-जेनेरिक वर्ग बेहतर होता है क्योंकि कॉलिंग सिंटैक्स अच्छा है और आप इसके भीतर विस्तार विधियां परिभाषित कर सकते हैं।

उत्तर

18

मैं प्रतिबिंब-भारी कोड कैशिंग के लिए स्थैतिक जेनेरिक कक्षाओं का उपयोग करता हूं।

मान लें कि मुझे एक अभिव्यक्ति वृक्ष बनाने की आवश्यकता है जो वस्तुओं को तुरंत चालू करे। मैं इसे कक्षा के स्थिर निर्माता में एक बार बना देता हूं, इसे लैम्ब्डा अभिव्यक्ति में संकलित करता हूं, फिर इसे स्थिर वर्ग के सदस्य में कैश करता हूं। मैं अक्सर इन वर्गों को सार्वजनिक रूप से निर्धारणीय नहीं बनाता - वे आम तौर पर अन्य वर्गों के लिए सहायक होते हैं। इस तरह से मेरी अभिव्यक्तियों को कैश करके, मैं कुछ प्रकार के Dictionary<Type, delegate> में अपनी अभिव्यक्तियों को कैश करने की आवश्यकता से बचता हूं।

BCL में इस पैटर्न का एक उदाहरण है। (DataRow) विस्तार विधियां Field<T>() और SetField<T>() (निजी) स्थैतिक जेनेरिक वर्ग System.Data.DataRowExtensions+UnboxT<T> का उपयोग करें। परावर्तक के साथ इसे देखें।

+1

उत्तर के हिस्से के रूप में स्थिर लैम्ब्डा अभिव्यक्ति बनाने वाले निर्माता का नमूना रखना जानकारीपूर्ण होगा। – Jim

3

पहले ब्लॉग पोस्ट आप का उल्लेख (एक ActiveRecord कार्यान्वयन के लिए एक स्थिर भंडार वर्ग के रूप में) एक वैध उपयोग दिखाता है:

public static class ArraySort<T> 
{ 
    public static T[] BubbleSort(T[] source) { } 

    public static T[] QuickSort(T[] source) { } 
} 
:

public static class Repository<T> 
{ 
    public static T[] FindAll { } 

    public static T GetById(int id){ } 

    public static void Save(T item) { } 
} 

या आप सामान्य सरणियों के लिए विभिन्न प्रकार के तरीकों को लागू करना चाहते थे

+2

मैं पर सामान्य मार्गदर्शन के लिए और अधिक देख रहा हूँ जब वे फायदेमंद होते हैं । गैर-सामान्य स्थैतिक वर्ग की तुलना में आपका # 2 बेहतर कैसे है जिसमें सामान्य तरीके हैं: - सार्वजनिक स्थैतिक टी [] बबलसार्ट (टी [] स्रोत)? –

+0

@ हाइटैक्राइडर स्टेक क्लास आपको निजी स्थिर संसाधनों को साझा करने की अनुमति देगा जो आपको आवश्यकतानुसार टी के प्रकार से मेल खाते हैं। अन्यथा, यह आपको कुछ कीस्ट्रोक बचाता है। –

1

आप सही हैं: वे अधिक उपयोग नहीं कर रहे हैं। शायद कुछ दुर्लभ मामले हैं जो अपवाद हैं, हालांकि। उदाहरण के लिए, यदि क्लास एक प्रकार-विशिष्ट भंडार था, जैसा कि जस्टिन के उदाहरण में था, लेकिन एक स्थिर संग्रह को कैश के रूप में रखा गया था? आम तौर पर, यदि इसमें राज्य शामिल है, न केवल विधियों, तो इसके लिए एक बिंदु हो सकता है।

+0

धन्यवाद, +1।अब तक हमारे पास है: उनका उपयोग करें (ए) यदि इसमें राज्य प्रति व्युत्पन्न-प्रकार है, या (बी) यदि आप सामान्य प्रकार (ओं) पर बाधा चाहते हैं और कम टाइप करना चाहते हैं। क्या यही है? –

+0

ठीक है, आप वास्तव में प्रति-विधि आधार पर बाधा डाल सकते हैं जब कक्षा स्वयं सामान्य नहीं है, इसलिए यह मुद्दा नहीं है, या तो। इसके अलावा, मुझे लगता है कि आपके पास मूल रूप से सही है। –

5

स्थिर जेनेरिक वर्गों का एक उपयोग जो मैंने हाल ही में सीखा था, वह वर्ग के स्तर पर बाधा को परिभाषित करना संभव था, फिर कक्षा के सभी स्थिर सदस्यों पर बाधा लागू होती है, जो मैंने सीखा है कि यह है स्थैतिक सामान्य वर्गों के लिए अनुमति नहीं है जो विस्तार विधियों को परिभाषित करते हैं।

उदाहरण के लिए यदि आप एक स्थैतिक जेनेरिक वर्ग बनाने जा रहे हैं और आप जानते हैं कि सभी सामान्य प्रकार आईसीओपरपेबल (केवल एक उदाहरण) होना चाहिए तो आप निम्न कार्य कर सकते हैं।

static class MyClass<T> where T : IComparable 
{ 
    public static void DoSomething(T a, T b) 
    { 
    } 

    public static void DoSomethingElse(T a, T b) 
    { 
    } 
} 

ध्यान दें कि मुझे सभी सदस्यों को बाधा लागू करने की आवश्यकता नहीं है, बल्कि केवल कक्षा स्तर पर।

8

सामान्य प्रकार के स्थिर क्षेत्र वास्तविक प्रकार टी के लिए विशिष्ट हैं। इसका मतलब है कि आप आंतरिक रूप से एक प्रकार का विशिष्ट कैश स्टोर कर सकते हैं। यह स्थैतिक जेनेरिक प्रकार बनाने का एक कारण हो सकता है।यहां एक (बल्कि बेकार, लेकिन जानकारीपूर्ण) उदाहरण है:

public static TypeFactory<T> where T : new() 
{ 
    // There is one slot per T. 
    private static readonly object instance = new T(); 

    public static object GetInstance() { 
     return instance; 
    } 
} 

string a = (string)TypeFactory<string>.GetInstance(); 
int b = (int)TypeFactory<int>.GetInstance(); 
+0

बहुत अच्छा। स्थैतिक जेनेरिक फैक्ट्री का उपयोग करके टी टी को कास्टिंग करने की आवश्यकता पूरी हुई। –

0

एक स्थैतिक जेनेरिक क्लास किसी भी स्थिर वर्ग के रूप में बिल्कुल उपयोगी है। अंतर यह है कि आपको प्रत्येक प्रकार के लिए स्थिर वर्ग का संस्करण बनाने के लिए कॉपी-एंड-पेस्ट का उपयोग करने की आवश्यकता नहीं है, जिसे आप काम करना चाहते हैं। आप क्लास जेनेरिक बनाते हैं, और आप प्रत्येक सेट प्रकार पैरामीटर के लिए एक संस्करण "जेनरेट" कर सकते हैं।

+0

लेकिन स्थिर वर्ग में जेनेरिक तरीके लगभग समान लाभ देते हैं (किसी भी टी के साथ काम करने के लिए वहां कोई कट और पेस्ट आवश्यक नहीं है), अंतर यह प्रतीत होता है कि ए) सामान्य स्थैतिक वर्ग में राज्य हो सकता है जो 'प्रति व्युत्पन्न-प्रकार' है 'सभी व्युत्पन्न वर्गों और बी के लिए केवल एक ही राज्य के विरोध में) कभी-कभी कक्षा के शीर्ष पर टी और इसकी बाधाओं को परिभाषित करने के लिए कम टाइपिंग होती है। क्या यही है? –

+0

आपने इसे काफी सही पाया है, लेकिन आप सुविधा को कम करके आंका रहे हैं। मैंने अभी एक प्रमुख रिफैक्टरिंग प्रयास के माध्यम से जाना है जिसके लिए सामान्य वर्गों, विधियों, इंटरफेस और प्रतिनिधियों के भारी उपयोग की आवश्यकता है। ऐसी कुछ स्थितियां थीं जहां आप उसी प्रकार के पैरामीटर का उपयोग करने के लिए _had_ थे, "प्रकार को पास करें" जहां से यह सभी तरह से तत्काल है। अन्यथा, प्रकार मेल नहीं खाते: एक विधि में टी टाइप करें दूसरे प्रकार में टाइप टी के समान नहीं हो सकता है। –

+0

लेकिन उनमें से कोई सामान्य * स्थैतिक * कक्षाएं थीं? स्पष्ट रूप से जेनेरिक * गैर स्थैतिक * कक्षाएं बेहद उपयोगी हैं क्योंकि आप विधियों के बीच टाइप की गई जानकारी साझा करते हैं, लेकिन एक सामान्य स्थैतिक वर्ग में केवल एक ही राज्य जिसे आप साझा कर सकते हैं वह स्थिर स्थिति है और ऐसा लगता है कि केवल बहुत सीमित परिदृश्य हैं जहां वे करेंगे कभी भी एक सामान्य गैर स्थैतिक वर्ग (आवश्यक होने पर सिंगलटन के रूप में तत्काल) या सामान्य स्थैतिक विधियों के साथ एक गैर-सामान्य स्थैतिक वर्ग के लिए बेहतर हो सकता है। –

2

स्टेटिक सी # में जेनेरिक क्लास के मुख्य उपयोग क्या हैं? उन्हें कब उपयोग किया जाना चाहिए? उनके उपयोग का सबसे अच्छा उदाहरण क्या है?

मुझे लगता है कि सामान्य रूप से, आपको स्थिर वर्गों पर प्रकार पैरामीटर बनाने से बचना चाहिए, अन्यथा आप अपने ग्राहक कोड को अधिक संक्षिप्त बनाने के लिए प्रकार-अनुमान पर निर्भर नहीं हो सकते हैं।

एक ठोस उदाहरण देने के लिए, मान लीजिए कि आप सूचियों पर संचालन को संभालने के लिए एक स्थिर उपयोगिता कक्षा लिख ​​रहे हैं। आप कक्षा को दो तरीकों से लिख सकते हैं:

// static class with generics 
public static class ListOps<T> 
{ 
    public static List<T> Filter(List<T> list, Func<T, bool> predicate) { ... } 
    public static List<U> Map<U>(List<T> list, Func<T, U> convertor) { ... } 
    public static U Fold<U>(List<T> list, U seed, Func<T, U> aggregator) { ... } 
} 

// vanilla static class 
public static class ListOps 
{ 
    public static List<T> Filter<T>(List<T> list, Func<T, bool> predicate) { ... } 
    public static List<U> Map<T, U>(List<T> list, Func<T, U> convertor) { ... } 
    public static U Fold<T, U>(List<T> list, U seed, Func<T, U> aggregator) { ... } 
} 

लेकिन कक्षाएं बराबर हैं, लेकिन कौन सा उपयोग करना आसान है? की तुलना करें:

// generic static class 
List<int> numbers = Enumerable.Range(0, 100).ToList(); 
List<int> evens = ListOps<int>.Filter(numbers, x => x % 2 = 0); 
List<string> numberString = ListOps<int>.Map(numbers, x => x.ToString()); 
int sumOfSquares = ListOps<int>.Fold(numbers, 0, (acc, x) => acc + x*x); 

// vanilla static class 
List<int> numbers = Enumerable.Range(0, 100).ToList(); 
List<int> evens = ListOps.Filter(numbers, x => x % 2 = 0); 
List<string> numberString = ListOps.Map(numbers, x => x.ToString()); 
int sumOfSquares = ListOps.Fold(numbers, 0, (acc, x) => acc + b * b); 

मेरी राय में, ListOps<someType> भारी और अनाड़ी है। वेनिला वर्ग की परिभाषा थोड़ी बड़ी है, लेकिन क्लाइंट कोड पढ़ने में आसान है - अनुमान लगाने के लिए धन्यवाद।

सबसे बुरे मामले में, सी # प्रकारों का अनुमान नहीं लगा सकता है, और आपको उन्हें हाथ से निर्दिष्ट करना होगा। क्या आप ListOps<A>.Map<B>(...) या ListOps.Map<A, B>(...) लिखेंगे? मेरी वरीयता बाद की ओर है।


रणनीति ऊपर विशेष रूप से अच्छी तरह से जब अपने स्थिर वर्ग रखती कोई परिवर्तनशील राज्य, या उसके परिवर्तनशील राज्य संकलन समय पर में जाना जाता है काम करता है।

यदि स्थैतिक वर्ग में उत्परिवर्तनीय राज्य होता है जिसका प्रकार संकलन समय पर निर्धारित नहीं होता है, तो संभवतः आपके पास सामान्य पैरा के साथ स्थिर वर्ग के लिए उपयोग का मामला है। उम्मीद है कि ये मौके कुछ और बहुत दूर हैं, लेकिन जब ऐसा होता है, तो आप खुश होंगे सी # कार्यक्षमता का समर्थन करता है।

+0

धन्यवाद। मुझे लगता है कि बाद वाले तर्क को एक्सटेंशन विधियों में भी बदला जा सकता है जो आपके उदाहरण के लिए विशेष रूप से प्रासंगिक है! एक बिंदु जो मुझे नहीं मिलता है, 'संकलन समय पर निर्धारित प्रकार' के बारे में अंतिम खंड नहीं है - जो गतिशील नहीं जेनरिक के लिए उपयोग की तरह लगता है जो * संकलित समय पर निर्धारित * हैं। क्या मुद्दा उबलता नहीं है "क्या सामान्य स्थैतिक वर्ग के प्रत्येक उप-वर्ग के लिए एक अलग परिवर्तनीय राज्य आवश्यक है या क्या सभी डेरिवेटिव्स के लिए केवल एक उत्परिवर्तनीय राज्य संग्रहीत है?" –

10

कक्षा static बनाना कोई कार्यक्षमता नहीं जोड़ता है - यदि आप बिना किसी इंस्टॉलेशन के कक्षा का उपयोग करना चाहते हैं तो यह एक सुविधाजनक जांच है। और इसके लिए कई उपयोग हैं ...

आप सीमा के आसपास काम करने के लिए स्थैतिक जेनेरिक कक्षाओं का उपयोग कर सकते हैं: सी # आंशिक विशेषज्ञता की अनुमति नहीं देता है। इसका मतलब है कि आपको या तो सभी प्रकार के पैरामीटर या कोई भी निर्दिष्ट करना होगा। हालांकि, यह अनिवार्य रूप से verbose हो सकता है।

उदाहरण के लिए:

static class Converter { 
    public TOut Convert<TIn, TOut>(TIn x) {...} 
} 

पिछले वर्ग के बाद से अनुमान वापसी मूल्यों पर काम नहीं करता है प्रकार निष्कर्ष की अनुमति नहीं देता। हालांकि, आप इनपुट प्रकार को निर्दिष्ट किए बिना रिटर्न वैल्यू प्रकार भी निर्दिष्ट नहीं कर सकते क्योंकि आप आंशिक रूप से विशेषज्ञ नहीं हो सकते हैं। (संभवत: स्थिर) सामान्य वर्ग का उपयोग करके आप दो प्रकार से केवल एक निर्दिष्ट कर सकते हैं:

static class ConvertTo<TOut> { 
    public TOut Convert<TIn>(TIn x) {...} 
} 

इस तरह आप पैरामीटर प्रकार पर प्रकार निष्कर्ष काम करते हैं और केवल वापसी प्रकार निर्दिष्ट कर सकते हैं।

(हालांकि उपर्युक्त मामला कल्पना योग्य है, लेकिन सामान्य वर्ग को निश्चित रूप से स्थैतिक होने की आवश्यकता नहीं है)।


दूसरे, (स्टीवन first pointed out के रूप में) एक अलग स्थिर क्षेत्र एक का निर्माण किया प्रकार के लिए मौजूद है, और है कि स्थिर कक्षाओं महान स्थानों प्रकार या प्रकार संयोजन के बारे में अतिरिक्त जानकारी संग्रहीत करने के लिए बनाता है। संक्षेप में, यह एक अर्ध-स्थिर हैशटेबल है जो कि प्रकारों पर कुंजी है।

प्रकारों पर एक अर्ध-स्थैतिक लुकअप टेबल कुंजीिंग एक छोटे से आर्केन्स लगता है, लेकिन यह वास्तव में एक बहुत ही उपयोगी संरचना है क्योंकि यह आपको महंगे प्रतिबिंब और कोड-जनरेशन परिणामों को स्टोर करने की अनुमति देती है जहां वे लगभग देखने के लिए स्वतंत्र हैं (एक शब्दकोश से सस्ता है क्योंकि इसमें जेआईटी-एड मिलता है और आप .GetType() पर कॉल से बचते हैं)। यदि आप मेटाप्रोग्रामिंग कर रहे हैं, तो यह बहुत अच्छा है!

उदाहरण के लिए, मैं ValueUtils में इस का उपयोग उत्पन्न हैश फंक्शन स्टोर करने के लिए:

//Hash any object: 
FieldwiseHasher.Hash(myCustomStructOrClass); 

//implementation: 
public static class FieldwiseHasher { 
    public static int Hash<T>(T val) { return FieldwiseHasher<T>.Instance(val); } 
} 

public static class FieldwiseHasher<T> { 
    public static readonly Func<T, int> Instance = CreateLambda().Compile(); 
    //... 
} 

स्टेटिक सामान्य तरीकों प्रकार अनुमान उपयोग वास्तव में आसान बनाने के लिए अनुमति देते हैं; जेनेरिक कक्षाओं पर स्थैतिक क्षेत्र वस्तुतः ओवरहेड-मुक्त संग्रहण (मेटा) डेटा की अनुमति देते हैं। अगर मुझे ओआरएम की तरह Dapper और PetaPoco इस तरह की तकनीकों का उपयोग करना है तो यह मुझे आश्चर्यचकित नहीं करेगा; लेकिन यह (डी) serializers के लिए भी महान है। एक सीमा यह है कि आपको कम ओवरहेड मिल रहा है क्योंकि आप संकलन-समय प्रकार के लिए बाध्यकारी हैं; यदि पारित वस्तु वास्तव में उप-वर्ग का एक उदाहरण है, तो आप शायद गलत प्रकार के लिए बाध्यकारी हो रहे हैं - और उस प्रकार से बचने के लिए चेक जोड़ने से कम ओवरहेड होने का लाभ कम हो जाता है।

2

मैं एंटीटीफ्रेमवर्क असिनक विधियों का उपयोग करने वाले वर्गों के परीक्षण के दौरान इन्हें डीबीसेट का नकल करने के लिए उपयोग करता हूं।

public static class DatabaseMockSetProvider<TEntity> where TEntity: class 
{ 
    public static DbSet<TEntity> CreateMockedDbSet(IQueryable<TEntity> mockData) 
    { 
     var mockSet = Mock.Create<DbSet<TEntity>>(); 
     Mock.Arrange(() => ((IDbAsyncEnumerable<TEntity>)mockSet).GetAsyncEnumerator()) 
      .Returns(new TestDbAsyncEnumerator<TEntity>(mockData.GetEnumerator())); 
     Mock.Arrange(() => ((IQueryable<TEntity>)mockSet).Provider) 
      .Returns(new TestDbAsyncQueryProvider<TEntity>(mockData.Provider)); 
     Mock.Arrange(() => ((IQueryable<TEntity>)mockSet).Expression).Returns(mockData.Expression); 
     Mock.Arrange(() => ((IQueryable<TEntity>)mockSet).ElementType).Returns(mockData.ElementType); 
     Mock.Arrange(() => ((IQueryable<TEntity>)mockSet).GetEnumerator()).Returns(mockData.GetEnumerator()); 

     return mockSet; 
    } 
} 

और मेरी इकाई परीक्षण में तो जैसे उन्हें इस्तेमाल - बहुत समय की बचत होती है और उन्हें किसी भी संस्था प्रकार के लिए उपयोग कर सकते हैं:

var mockSet = DatabaseMockSetProvider<RecipeEntity>.CreateMockedDbSet(recipes); 
     Mock.Arrange(() => context.RecipeEntities).ReturnsCollection(mockSet); 
संबंधित मुद्दे