2010-10-28 9 views
19

मैं एक कक्षा लिख ​​रहा हूं जो अनिवार्य रूप से सी # में प्रत्येक आदिम संख्यात्मक प्रकारों के लिए समान प्रकार की गणना करता है। यद्यपि असली गणना अधिक जटिल है, इसके बारे में सोचें कि कई मूल्यों की औसत गणना करने के लिए एक विधि के रूप में, उदाहरण के लिएजेनेरिक सी # कोड और प्लस ऑपरेटर

class Calc<T> 
{ 
    public T Count { get; private set; } 
    public T Total { get; private set; } 
    public T Average { get { return Count/Total; } } 
    public T AddDataPoint(T data) 
    { 
     Total += data; 
     Count++; 
    } 
} 

दुर्भाग्य से सी # करने में असमर्थ है:

class Calc 
{ 
    public int Count { get; private set; } 
    public int Total { get; private set; } 
    public int Average { get { return Count/Total; } } 
    public int AddDataPoint(int data) 
    { 
     Total += data; 
     Count++; 
    } 
} 

अब डबल, नाव और शायद अन्य वर्गों को परिभाषित ऑपरेटर + और ऑपरेटर के लिए है कि एक ही आपरेशन का समर्थन करने के लिए /, तो मेरा पहला विचार था बस जेनरिक उपयोग करने के लिए यह निर्धारित करें कि टी ऑपरेटरों का समर्थन करता है + और/इसलिए उपरोक्त स्निपेट को संकलित नहीं करता है। मेरा अगला विचार उन ऑपरेटरों का समर्थन करने वाले प्रकारों को टी को बाधित करना था, लेकिन मेरा प्रारंभिक शोध इंगित करता है कि यह नहीं किया जा सकता है।

यह निश्चित रूप से संभव है कि मैं उन वर्गों में से प्रत्येक बॉक्स को समर्थन देना चाहता हूं जो एक कस्टम इंटरफ़ेस लागू करता है उदा। IMath और उस पर टी को प्रतिबंधित करें, लेकिन इस कोड को कई बार बुलाया जाएगा और मैं मुक्केबाजी ओवरहेड से बचना चाहता हूं।

क्या कोड डुप्लिकेशन के बिना इसे हल करने का एक सुरुचिपूर्ण और कुशल तरीका है?

+1

क्या आप केवल LINQ का उपयोग नहीं कर सकते? IENumerable पर योग और औसत समर्थित हैं। – Mathias

+2

इस अन्य SO प्रश्न को देखें: http://stackoverflow.com/questions/32664/c-generic-constraint-for-only-integers – spinon

+0

@Mathias: नहीं, मैंने इसे वास्तविक गणना के सरलीकृत उदाहरण के रूप में उपयोग किया। –

उत्तर

13

मैं अभिव्यक्तियों का उपयोग करके समाप्त हुआ, मार्क ग्रेवेल द्वारा उल्लिखित एक दृष्टिकोण जिसे मैंने स्पिनन की टिप्पणी से लिंक का पालन करके पाया।

http://www.yoda.arachsys.com/csharp/genericoperators.html

+0

बस यह सुनिश्चित करें कि आप प्रतिनिधियों को कैश करें और उनका पुन: उपयोग करें; पी –

+3

ध्यान दें कि MiscUtils में एक पूर्व-निर्मित 'ऑपरेटर' श्रेणी है जो आपको आवश्यक सभी आवश्यक उपयोगिता विधियों को प्रदान करती है। –

+0

@Marc: हाँ, मैंने आपकी टिप्पणी लागू की ;-) –

2

वहाँ सी # 4.0 में गतिशील का उपयोग कर एक दृष्टिकोण है, यह स्पष्ट रूप से सही नहीं है, लेकिन यह बात करने के लिए एक नया प्रकाश ला सकता है।

http://www.codeproject.com/KB/cs/genericnumerics.aspx

यह समाधान एक में सामान्य प्रकार की कमी का उपयोग करता है:

विवरण in this blog post

+0

द्वारा एक अच्छा समाधान है 'गतिशील' के साथ समस्या यह है कि यह एक और स्तर का संकेत प्रदान करता है। मैं बड़ी संख्या में जटिल गणना और संदिग्ध (लेकिन ईमानदार होने के बारे में नहीं जानता) कर रहा हूं कि इंडेक्शन का समग्र ऐप प्रदर्शन पर एक मापनीय प्रभाव होगा। –

1

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

4

(मुझे क्षमा करें यदि मैं आज इसे पोस्ट, लेकिन मैं एक जगह है जहाँ कोड के इस टुकड़े डाल करने के लिए की तलाश में गया था, और इस सवाल का सही लग रहा था)

Gravell के लेख पर एक विस्तार के रूप में:

int sum = Add<int>.Do(x, y); 

लाभ यह है कि हम सुरक्षित रखने के लिए नेट के प्रकार प्रणाली का उपयोग Add और creat के विभिन्न "वेरिएंट" है:

public static class Add<T> 
{ 
    public static readonly Func<T, T, T> Do; 

    static Add() 
    { 
     var par1 = Expression.Parameter(typeof(T)); 
     var par2 = Expression.Parameter(typeof(T)); 

     var add = Expression.Add(par1, par2); 

     Do = Expression.Lambda<Func<T, T, T>>(add, par1, par2).Compile(); 
    } 
} 

आप इसे पसंद का उपयोग यदि आवश्यक हो तो नए लोगों को आईएनजी करना। तो पहली बार जब आप Add<int>.Do(...) पर कॉल करते हैं तो Expression बनाया जाएगा, लेकिन यदि आप इसे दूसरी बार कॉल करते हैं, तो Add<int> पहले ही पूरी तरह से प्रारंभ हो जाएगा।

कुछ साधारण बेंचमार्क पर, यह प्रत्यक्ष जोड़ से 2x धीमी है। मुझे लगता है कि यह बहुत अच्छा है। आह ... यह उन वस्तुओं के साथ संगत है जो operator+ को फिर से परिभाषित करते हैं। स्पष्ट रूप से अन्य परिचालनों का निर्माण करना आसान है।

से Meirion ह्यूजेस

विधि अलावा मेटा-कोडिंग ताकि आप T1आपरेशनT2 के मामलों को संभाल सकता है के साथ बढ़ाया जा सकता है। उदाहरण के लिए, यदि T1 एक संख्या है, तो इसे operator * से पहले T2 == double में परिवर्तित करने की आवश्यकता है, फिर इसे वापस परिवर्तित कर दें। जबकि T1Foo और Foo में T2 == double के साथ गुणा करने के लिए ऑपरेटर है, तो आप रूपांतरण को छोड़ सकते हैं। try, catch आवश्यक है क्योंकि यह जांचने का सबसे आसान तरीका है कि T operator *(T, double) मौजूद है या नहीं।

public static class Scale<T> 
{ 
    public static Func<T, double, T> Do { get; private set; } 

    static Scale() 
    { 
     var par1 = Expression.Parameter(typeof(T)); 
     var par2 = Expression.Parameter(typeof(double)); 

     try 
     { 
      Do = Expression 
       .Lambda<Func<T, double, T>>(
        Expression.Multiply(par1, par2), 
        par1, par2) 
       .Compile(); 
     } 
     catch 
     { 
      Do = Expression 
       .Lambda<Func<T, double, T>>(
        Expression.Convert(
         Expression.Multiply(
          Expression.Convert(par1, typeof (double)), 
          par2), 
         typeof(T)), 
        par1, par2) 
       .Compile(); 
     } 
    } 
} 
+0

@Meirion मुझे बहुत कुछ पसंद नहीं आया जो आपने किया (संपादन द्वारा जोड़ना) ... लेकिन कोड साफ था। मैंने थोड़ी सी संपादित की है और थोड़ा सा स्पष्टीकरण बदल दिया है – xanatos

+0

हाँ मुझे पता है, क्षमा करें। मैंने सोचा कि यह उपयोगी होगा (यह मेरे लिए था), और मैं इसे अलग जवाब के रूप में जोड़ने वाला था, लेकिन यह धागा बंद कर दिया गया है और दूसरा धागा ऑफ-विषय होगा। :/ –

+0

@MeirionHughes अगली बार, अपना नाम टेक्स्ट के अतिरिक्त टुकड़े पर रखें, जैसे मैंने किया * मीरियन ह्यूजेस * से जोड़ा *, तो यह स्पष्ट है कि लेखक 1 से क्या है और लेखक 2 से क्या है – xanatos

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