क्या कोई बाधा है जो मेरे सामान्य तरीके को संख्यात्मक प्रकारों तक सीमित करती है?


364

क्या कोई मुझे बता सकता है कि क्या सामान्य प्रकार के तर्क Tको सीमित करने के लिए जेनेरिक के साथ एक तरीका है :

  • Int16
  • Int32
  • Int64
  • UInt16
  • UInt32
  • UInt64

मैं whereकीवर्ड से अवगत हूं , लेकिन केवल एक इंटरफ़ेस नहीं ढूँढ सकता इन प्रकारों के ,

कुछ इस तरह:

static bool IntegerFunction<T>(T value) where T : INumeric 

जवाबों:


140

C # इसका समर्थन नहीं करता है। हेजलबर्ग ने ब्रूस एकेल के साथ एक साक्षात्कार में फीचर को लागू नहीं करने के कारणों का वर्णन किया है :

और यह स्पष्ट नहीं है कि जोड़ा जटिलता उस छोटी उपज के लायक है जो आपको मिलती है। यदि आप जो कुछ करना चाहते हैं वह सीधे बाधा प्रणाली में समर्थित नहीं है, तो आप इसे कारखाने के पैटर्न के साथ कर सकते हैं। Matrix<T>उदाहरण के लिए, आप एक हो सकते हैं , और इसमें Matrixआप एक डॉट उत्पाद विधि को परिभाषित करना चाहेंगे। यही कारण है कि पाठ्यक्रम है कि इसका मतलब है आप अंततः को समझने के लिए गुणा दो की जरूरत की Tहै, लेकिन आपको लगता है कि यह नहीं कह सकते की कोई समस्या के रूप में, कम से कम नहीं तो Tहै int, doubleया float। लेकिन आप क्या कर सकते हैं आपके पास Matrixएक तर्क के रूप में एक लेना है Calculator<T>, और Calculator<T>, एक विधि है जिसे कहा जाता है multiply। आप इसे लागू करते हैं और आप इसे पास करते हैं Matrix

हालांकि, यह काफी जटिल कोड की ओर जाता है, जहां उपयोगकर्ता को अपने स्वयं के Calculator<T>कार्यान्वयन की आपूर्ति करनी होती है, प्रत्येक के लिए Tजिसे वे उपयोग करना चाहते हैं। जब तक यह एक्स्टेंसिबल नहीं होता है, यानी यदि आप केवल एक निश्चित संख्या के प्रकार का समर्थन करना चाहते हैं, जैसे कि intऔर double, तो आप अपेक्षाकृत सरल इंटरफ़ेस से दूर हो सकते हैं:

var mat = new Matrix<int>(w, h);

( GitHub Gist में न्यूनतम कार्यान्वयन। )

हालांकि, जैसे ही आप चाहते हैं कि उपयोगकर्ता अपने स्वयं के, कस्टम प्रकारों की आपूर्ति करने में सक्षम हो, आपको इस कार्यान्वयन को खोलने की आवश्यकता है ताकि उपयोगकर्ता अपने स्वयं के Calculatorउदाहरणों की आपूर्ति कर सके । उदाहरण के लिए, कस्टम मैट्रिक्स फ़्लोटिंग पॉइंट कार्यान्वयन का उपयोग करने वाले मैट्रिक्स को त्वरित करने के लिए, DFPआपको यह कोड लिखना होगा:

var mat = new Matrix<DFP>(DfpCalculator.Instance, w, h);

... और सभी सदस्यों के लिए लागू करें DfpCalculator : ICalculator<DFP>

एक विकल्प, जो दुर्भाग्य से समान सीमाओं को साझा करता है, नीति वर्गों के साथ काम करना है, जैसा कि सर्गेई शैंडर के जवाब में चर्चा की गई है


25
btw, MiscUtil एक सामान्य वर्ग प्रदान करता है जो ठीक यही करता है; Operator/ Operator<T>; yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html
मार्क Gravell

1
@ मर्क: अच्छी टिप्पणी। हालाँकि, बस स्पष्ट होने के लिए, मुझे नहीं लगता कि हेजलबर्ग समस्या के समाधान के रूप में कोड पीढ़ी का उल्लेख कर रहे थे जैसा कि आप Operator<T>कोड में करते हैं (क्योंकि साक्षात्कार Expressionsरूपरेखा के अस्तित्व से बहुत पहले दिया गया था , भले ही कोई भी शामिल हो सकता है पाठ्यक्रम का उपयोग करें Reflection.Emit) - और मैं वास्तव में उनके समाधान में दिलचस्पी होगी ।
कोनराड रुडोल्फ

@ कोनराड रुडोल्फ: मुझे लगता है कि इसी तरह के सवाल का जवाब हेजलबर्ग के काम के बारे में बताता है। अन्य सामान्य वर्ग को अमूर्त बनाया गया है। चूंकि आपको प्रत्येक प्रकार के समर्थन के लिए दूसरे सामान्य वर्ग को लागू करने की आवश्यकता होती है, इसलिए इसका परिणाम डुप्लिकेट कोड में होगा, लेकिन इसका मतलब यह है कि आप केवल एक समर्थित प्रकार के साथ मूल सामान्य वर्ग को तुरंत ही भेज सकते हैं।
इरगवुन

14
मैं हाइज्सबर्ग के वाक्यांश से सहमत नहीं हूं "तो एक अर्थ में, सी ++ टेम्पलेट वास्तव में अप्रतिष्ठित, या शिथिल टाइप हैं। जबकि सी # जेनरिक दृढ़ता से टाइप किए गए हैं।" यह वास्तव में सी # को बढ़ावा देने के लिए मार्केटिंग बीएस है। मजबूत / कमजोर टाइपिंग का निदान की गुणवत्ता से कोई लेना-देना नहीं है। अन्यथा: दिलचस्प लगता है।
सेबस्टियन मच

100

इस सवाल की लोकप्रियता और इस तरह के एक समारोह के पीछे की दिलचस्पी को देखते हुए मैं यह देखकर हैरान हूं कि अभी तक टी 4 से जुड़ा कोई जवाब नहीं है।

इस सैंपल कोड में मैं एक बहुत ही सरल उदाहरण प्रदर्शित करता हूँ कि आप शक्तिशाली टेंपलेटिंग इंजन का उपयोग कैसे कर सकते हैं यह करने के लिए कि कंपाइलर जेनेरिक के साथ पर्दे के पीछे क्या करता है।

हुप्स के माध्यम से जाने और संकलन-समय निश्चितता का त्याग करने के बजाय, आप बस उस फ़ंक्शन को उत्पन्न कर सकते हैं जिसे आप हर प्रकार के लिए चाहते हैं और उसी के अनुसार उपयोग करें (संकलन समय पर!)।

ऐसा करने के लिए:

  • GenericNumberMethodTemplate.tt नामक एक नया टेक्स्ट टेम्प्लेट फ़ाइल बनाएं
  • स्वतः जनरेट किया गया कोड निकालें (आप इसे ज़्यादातर रखेंगे, लेकिन कुछ की आवश्यकता नहीं है)।
  • निम्नलिखित स्निपेट जोड़ें:
<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>

<# Type[] types = new[] {
    typeof(Int16), typeof(Int32), typeof(Int64),
    typeof(UInt16), typeof(UInt32), typeof(UInt64)
    };
#>

using System;
public static class MaxMath {
    <# foreach (var type in types) { 
    #>
        public static <#= type.Name #> Max (<#= type.Name #> val1, <#= type.Name #> val2) {
            return val1 > val2 ? val1 : val2;
        }
    <#
    } #>
}

बस। अब आप कर रहे हैं।

इस फ़ाइल को सहेजना स्वचालित रूप से इस स्रोत फ़ाइल में संकलित करेगा:

using System;
public static class MaxMath {
    public static Int16 Max (Int16 val1, Int16 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static Int32 Max (Int32 val1, Int32 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static Int64 Max (Int64 val1, Int64 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt16 Max (UInt16 val1, UInt16 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt32 Max (UInt32 val1, UInt32 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt64 Max (UInt64 val1, UInt64 val2) {
        return val1 > val2 ? val1 : val2;
    }
}

अपनी mainविधि में आप सत्यापित कर सकते हैं कि आपके पास निश्चित समय है:

namespace TTTTTest
{
    class Program
    {
        static void Main(string[] args)
        {
            long val1 = 5L;
            long val2 = 10L;
            Console.WriteLine(MaxMath.Max(val1, val2));
            Console.Read();
        }
    }
}

यहां छवि विवरण दर्ज करें

मैं एक टिप्पणी से आगे निकलूंगा: नहीं, यह DRY सिद्धांत का उल्लंघन नहीं है। DRY सिद्धांत लोगों को कई स्थानों पर कोड को डुप्लिकेट करने से रोकने के लिए है जिससे एप्लिकेशन को बनाए रखने में कठिन हो जाएगा।

यह यहाँ बिल्कुल भी नहीं है: यदि आप एक बदलाव चाहते हैं तो आप सिर्फ टेम्पलेट (अपनी सभी पीढ़ी के लिए एक स्रोत!) को बदल सकते हैं और यह पूरा हो गया है।

अपनी स्वयं की कस्टम परिभाषाओं के साथ इसका उपयोग करने के लिए, अपने जनरेट किए गए कोड में एक नाम स्थान की घोषणा जोड़ें (सुनिश्चित करें कि यह वही है जहाँ आप अपने कार्यान्वयन को परिभाषित करेंगे) और वर्ग के रूप में चिह्नित करें partial। बाद में, इन पंक्तियों को अपनी टेम्पलेट फ़ाइल में जोड़ें ताकि यह अंतिम संकलन में शामिल हो जाए:

<#@ import namespace="TheNameSpaceYouWillUse" #>
<#@ assembly name="$(TargetPath)" #>

चलो ईमानदार रहें: यह बहुत अच्छा है।

डिस्क्लेमर: यह नमूना केविन हज़र्ड और जेसन बॉक, मैनिंग प्रकाशनों द्वारा .NET में मेटाप्रोग्रामिंग से बहुत प्रभावित हुआ है ।


यह बहुत अच्छा है, लेकिन क्या इस समाधान को संशोधित करना संभव होगा ताकि तरीकों को कुछ सामान्य प्रकारों को स्वीकार किया जा सके Tया जो विभिन्न IntXवर्गों से विरासत में मिला हो ? मुझे यह समाधान पसंद है क्योंकि यह समय बचाता है, लेकिन इसके लिए 100% मुद्दे को हल करता है (उतना अच्छा नहीं होने के बावजूद अगर सी # को इस प्रकार की बाधा के लिए समर्थन था, बिल्ट-इन) उत्पन्न विधियों में से प्रत्येक अभी भी सामान्य होना चाहिए ताकि वे एक प्रकार की एक वस्तु लौटा सकते हैं जो एक IntXXवर्ग से विरासत में मिली है ।
ज़ाचरी न्येबेल

1
@ZacharyKniebel: IntXXप्रकार वे संरचनाएं हैं जिनका अर्थ है कि वे पहली जगह में विरासत का समर्थन नहीं करते हैं । और अगर यह तब भी होता है तो Liskov प्रतिस्थापन सिद्धांत (जिसे आप SOLID मुहावरे से जान सकते हैं) लागू होता है: यदि विधि के रूप में परिभाषित किया गया है Xऔर प्रति परिभाषा Yका एक बच्चा है Xतो किसी भी Yविकल्प के रूप में उस पद्धति को पारित करने में सक्षम होना चाहिए इसका आधार प्रकार
जीरो वेनवेल

1
नीतियों का उपयोग कर इस तरीके को stackoverflow.com/questions/32664/... टी -4 उपयोग वर्गों उत्पन्न करने के लिए करता है।
सेर्गेई शैंडर

2
इस समाधान के लिए +1 चूंकि यह नीति आधारित समाधानों के विपरीत, अंतर्निहित अभिन्न प्रकारों की संचालन दक्षता को संरक्षित करता है। एक अतिरिक्त (संभवतः आभासी) पद्धति के माध्यम से निर्मित सीएलआर ऑपरेटरों (जैसे ऐड) को कॉल करना कई बार (गणितीय पुस्तकालयों की तरह) उपयोग किए जाने पर प्रदर्शन को गंभीर रूप से प्रभावित कर सकता है। और चूंकि अभिन्न प्रकार की संख्या स्थिर है (और इससे विरासत में प्राप्त नहीं किया जा सकता) आपको केवल बग फिक्स के लिए कोड को पुन: उत्पन्न करने की आवश्यकता है।
अत्तिला क्लैनिक

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

86

इसके लिए कोई अड़चन नहीं है। यह सांख्यिक गणना के लिए जेनरिक का उपयोग करने के इच्छुक किसी भी व्यक्ति के लिए एक वास्तविक मुद्दा है।

मैं आगे जाऊंगा और कहूंगा कि हमें जरूरत है

static bool GenericFunction<T>(T value) 
    where T : operators( +, -, /, * )

या और भी

static bool GenericFunction<T>(T value) 
    where T : Add, Subtract

दुर्भाग्य से आपके पास केवल इंटरफेस, आधार वर्ग और कीवर्ड struct(मूल्य-प्रकार classहोना चाहिए ), (संदर्भ प्रकार होना चाहिए) और new()(डिफ़ॉल्ट डिफ़ॉल्ट प्रोसेसर होना चाहिए)

आप यहां कोडप्रोजेक्ट कीINullable<T> तरह संख्या को कुछ और (समान ) में लपेट सकते हैं ।


आप रनटाइम पर प्रतिबंध लागू कर सकते हैं (ऑपरेटरों के लिए दर्शाते हुए या प्रकारों के लिए जाँच करके) लेकिन यह पहली जगह में सामान्य होने का लाभ खो देता है।


2
मुझे आश्चर्य है अगर आप सामान्य ऑपरेटरों ... के लिए MiscUtil के समर्थन देखा है yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html
मार्क Gravell

10
हाँ - जॉन स्कीट ने कुछ समय पहले (लेकिन इस साल पुरानी प्रतिक्रिया के बाद) मुझे उनके लिए इशारा किया - वे एक चतुर विचार हैं, लेकिन मैं अभी भी उचित बाधा समर्थन करना चाहूंगा।
कीथ

1
रुको, where T : operators( +, -, /, * )कानूनी C # है? नौसिखिया प्रश्न के लिए क्षमा करें।
kdbanman

@kdbanman मुझे ऐसा नहीं लगता। कीथ कह रहे हैं कि C # समर्थन नहीं करता है जो ओपी पूछ रहा है, और यह सुझाव दे रहा है कि हमें करने में सक्षम होना चाहिए where T : operators( +, -, /, * ), लेकिन नहीं।
AMTerp

62

नीतियों का उपयोग करके समाधान:

interface INumericPolicy<T>
{
    T Zero();
    T Add(T a, T b);
    // add more functions here, such as multiplication etc.
}

struct NumericPolicies:
    INumericPolicy<int>,
    INumericPolicy<long>
    // add more INumericPolicy<> for different numeric types.
{
    int INumericPolicy<int>.Zero() { return 0; }
    long INumericPolicy<long>.Zero() { return 0; }
    int INumericPolicy<int>.Add(int a, int b) { return a + b; }
    long INumericPolicy<long>.Add(long a, long b) { return a + b; }
    // implement all functions from INumericPolicy<> interfaces.

    public static NumericPolicies Instance = new NumericPolicies();
}

एल्गोरिदम:

static class Algorithms
{
    public static T Sum<P, T>(this P p, params T[] a)
        where P: INumericPolicy<T>
    {
        var r = p.Zero();
        foreach(var i in a)
        {
            r = p.Add(r, i);
        }
        return r;
    }

}

उपयोग:

int i = NumericPolicies.Instance.Sum(1, 2, 3, 4, 5);
long l = NumericPolicies.Instance.Sum(1L, 2, 3, 4, 5);
NumericPolicies.Instance.Sum("www", "") // compile-time error.

समाधान संकलन-समय सुरक्षित है। CityLizard फ्रेमवर्क .NET 4.0 के लिए संकलित संस्करण प्रदान करता है। फ़ाइल lib / NETFramework4.0 / CityLizard.Policy.dll है।

यह Nuget: https://www.nuget.org/packages/CityLizard/ में भी उपलब्ध है । CityLizard.Policy.I संरचना देखें ।


इस पैटर्न के साथ मेरे पास समस्याएँ थीं जब सामान्य पैरामीटर की तुलना में कम फ़ंक्शन तर्क हैं। खोला stackoverflow.com/questions/36048248/...
xvan

किसी भी कारण का उपयोग क्यों struct? क्या होगा अगर मैं इसके बजाय सिंगलटन-क्लास का उपयोग करता हूं और उदाहरण के लिए public static NumericPolicies Instance = new NumericPolicies();और फिर इस कंस्ट्रक्टर को जोड़ता हूं private NumericPolicies() { }
मकाज़म अख़गरी

@ M.kazemAkhgary आप सिंगलटन का उपयोग कर सकते हैं। मैं संरचना पसंद करते हैं। सिद्धांत रूप में, यह संकलक / सीएलआर द्वारा अनुकूलित किया जा सकता है क्योंकि संरचना में कोई जानकारी नहीं है। सिंगलटन के मामले में, आप अभी भी एक संदर्भ पारित करेंगे, जो जीसी पर अतिरिक्त दबाव जोड़ सकता है। एक और लाभ यह है कि संरचना शून्य नहीं हो सकती है :-)।
सेर्गेई शैंडर

मैं यह कहने जा रहा था कि आपने एक बहुत ही स्मार्ट समाधान पाया है, लेकिन समाधान मेरे लिए बहुत सीमित है: मैं इसका उपयोग करने जा रहा था T Add<T> (T t1, T t2), लेकिन Sum()केवल तब काम करता है जब यह इसे पुनः प्राप्त कर सकता है, यह इसके मापदंडों से टी का अपना प्रकार है, जो संभव नहीं है जब यह एक और सामान्य कार्य में एम्बेडेड है।
टोबियास नाइट

16

यह प्रश्न एक बार पूछे जाने वाले प्रश्न का एक सा है, इसलिए मैं इसे विकी के रूप में पोस्ट कर रहा हूं (क्योंकि मैंने पहले भी इसी तरह पोस्ट किया है, लेकिन यह एक पुराना है); वैसे भी ...

.NET का कौन सा संस्करण उपयोग कर रहे हैं? यदि आप .NET 3.5 का उपयोग कर रहे हैं, तो मेरे पास MiscUtil (फ्री आदि) में एक सामान्य ऑपरेटर कार्यान्वयन है ।

इसमें T Add<T>(T x, T y)विभिन्न प्रकारों (जैसे DateTime + TimeSpan) पर अंकगणित के लिए और अन्य प्रकार जैसे तरीके हैं ।

इसके अतिरिक्त, यह सभी इनबिल्ट, लिफ्टेड और बीस्पोक ऑपरेटरों के लिए काम करता है, और प्रदर्शन के लिए प्रतिनिधि को कैश करता है।

क्यों यह मुश्किल है पर कुछ अतिरिक्त पृष्ठभूमि है यहाँ

आप यह भी जानना चाहते होंगे कि dynamic(4.0) सॉर्ट-ऑफ इस मुद्दे को अप्रत्यक्ष रूप से भी - यानी

dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect

14

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

मुझे लगता है कि एकमात्र समाधान एक रनटाइम जांच करना है जो दुर्भाग्य से समस्या को संकलन समय पर उठाए जाने से रोकता है। वह कुछ इस तरह से जाना होगा: -

static bool IntegerFunction<T>(T value) where T : struct {
  if (typeof(T) != typeof(Int16)  &&
      typeof(T) != typeof(Int32)  &&
      typeof(T) != typeof(Int64)  &&
      typeof(T) != typeof(UInt16) &&
      typeof(T) != typeof(UInt32) &&
      typeof(T) != typeof(UInt64)) {
    throw new ArgumentException(
      string.Format("Type '{0}' is not valid.", typeof(T).ToString()));
  }

  // Rest of code...
}

जो थोड़ा बदसूरत है मुझे पता है, लेकिन कम से कम आवश्यक बाधाओं को प्रदान करता है।

मैं इस कार्यान्वयन के लिए संभावित प्रदर्शन निहितार्थों पर भी ध्यान देना चाहूंगा, शायद वहाँ एक तेज़ तरीका है।


13
+1, हालाँकि, // Rest of code...यह संकलित नहीं किया जा सकता है यदि यह बाधाओं द्वारा परिभाषित संचालन पर निर्भर करता है।
निक

1
Convert.ToIntXX (मान) "// बाकी कोड" संकलित करने में मदद कर सकता है - कम से कम जब तक इंटेगरफंक्शन का रिटर्न प्रकार भी टी का नहीं होता है, तब आप हूप्ड होते हैं। :-पी
योयो

-1; यह @Nick द्वारा दिए गए कारण के लिए काम नहीं करता है। जैसे ही आप किसी भी अंकगणितीय आपरेशनों करने की कोशिश // Rest of code...की तरह value + valueया value * value, आप एक संकलन त्रुटि मिल गया है।
मार्क अमेरी

13

संभवतः आप जितना करीबी कर सकते हैं

static bool IntegerFunction<T>(T value) where T: struct

यकीन नहीं तो आप निम्नलिखित कर सकते हैं

static bool IntegerFunction<T>(T value) where T: struct, IComparable
, IFormattable, IConvertible, IComparable<T>, IEquatable<T>

कुछ विशिष्ट के लिए, प्रत्येक प्रकार के लिए केवल ओवरलोड क्यों नहीं हैं, सूची इतनी कम है और इसमें संभवतः कम स्मृति पदचिह्न होंगे।


6

C # 7.3 के साथ शुरुआत करके, आप यह निर्दिष्ट करने के लिए क़रीब- क़रीब - अप्रबंधित बाधा का उपयोग कर सकते हैं कि एक प्रकार का पैरामीटर एक नॉन-पॉइंटर, नॉन-न्युलेबल अनवांटेड टाइप है।

class SomeGeneric<T> where T : unmanaged
{
//...
}

मानवरहित बाधा से तात्पर्य संरचना में बाधा से है और इसे संरचना या नए () बाधाओं से नहीं जोड़ा जा सकता है।

एक प्रकार एक अप्रबंधित प्रकार है यदि यह निम्नलिखित में से कोई एक प्रकार है:

  • Sbyte, बाइट, लघु, ushort, int, यूइंट, लॉन्ग, ulong, char, float, डबल, दशमलव या बूल
  • किसी भी प्रकार का
  • कोई सूचक प्रकार
  • कोई भी उपयोगकर्ता-परिभाषित संरचना प्रकार जिसमें केवल और # C 7.3 7.3 और पहले के मानव रहित प्रकार के फ़ील्ड शामिल हैं, एक निर्मित प्रकार नहीं है (एक प्रकार जिसमें कम से कम एक प्रकार का तर्क शामिल है)

आगे और प्रतिबंधित करने के लिए सूचक और उपयोगकर्ता-परिभाषित प्रकार जो IComparable add IComparable को लागू नहीं करते हैं (लेकिन Enum अभी भी IComparable से लिया गया है, इसलिए IEquitable <T> जोड़कर enum को प्रतिबंधित करें, आप अपनी परिस्थितियों के आधार पर आगे जा सकते हैं और अतिरिक्त इंटरफेस जोड़ सकते हैं। अप्रबंधित इस सूची को कम रखने की अनुमति देता है):

    class SomeGeneric<T> where T : unmanaged, IComparable, IEquatable<T>
    {
    //...
    }

अच्छा है, लेकिन पर्याप्त नहीं ... उदाहरण के लिए, बाधा के DateTimeतहत आता है unmanaged, IComparable, IEquatable<T>..
एडम कैल्वेट बोहल

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

4

टेम्पलेट्स को प्रकारों तक सीमित करने का कोई तरीका नहीं है, लेकिन आप प्रकार के आधार पर विभिन्न कार्यों को परिभाषित कर सकते हैं। एक सामान्य संख्यात्मक पैकेज के हिस्से के रूप में, मुझे दो मूल्यों को जोड़ने के लिए एक सामान्य वर्ग की आवश्यकता थी।

    class Something<TCell>
    {
        internal static TCell Sum(TCell first, TCell second)
        {
            if (typeof(TCell) == typeof(int))
                return (TCell)((object)(((int)((object)first)) + ((int)((object)second))));

            if (typeof(TCell) == typeof(double))
                return (TCell)((object)(((double)((object)first)) + ((double)((object)second))));

            return second;
        }
    }

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

        internal static int Sum(int first, int second)
        {
            return first + second;
        }

एक अनुभवजन्य समाधान प्रदान करने के लिए धन्यवाद!
zsf222 12

क्या यह प्रत्येक प्रकार के लिए समान विधि बनाने की तुलना में समान नहीं है?
लुइस

3

मैंने इन समस्याओं को हल करने के लिए थोड़ी लाइब्रेरी कार्यक्षमता बनाई:

के बजाय:

public T DifficultCalculation<T>(T a, T b)
{
    T result = a * b + a; // <== WILL NOT COMPILE!
    return result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Should result in 8.

आप लिख सकते हैं:

public T DifficultCalculation<T>(Number<T> a, Number<T> b)
{
    Number<T> result = a * b + a;
    return (T)result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Results in 8.

आप स्रोत कोड यहां पा सकते हैं: /codereview/26022/improvement-requested-for-generic-calculator-and-generic-number


2

मैं समज के समान सोच रहा था, केवल पूर्णांकों के लिए ही क्यों? और अगर ऐसा है, तो आप एक सहायक वर्ग या ऐसा कुछ बनाना चाहते हैं, जो आप चाहते हैं कि सभी प्रकार पकड़ सकें।

यदि आप चाहते हैं कि सभी पूर्णांक हैं, तो जेनेरिक का उपयोग न करें, यह सामान्य नहीं है; या बेहतर अभी तक, इसके प्रकार की जाँच करके किसी अन्य प्रकार को अस्वीकार करें।


2

इसके लिए अभी तक कोई 'अच्छा' समाधान नहीं है। हालाँकि आप टाइप तर्क को अपने काल्पनिक 'इन्युमेरिक' बाधा के लिए कई प्रक्षेपास्त्रों को खारिज करने के लिए काफी हद तक संकीर्ण कर सकते हैं जैसा कि हैकड ने ऊपर दिखाया है।

स्थिर बूल इंटेगरफंक्शन <टी> (टी मूल्य) जहां टी: IComparable, IFormattable, IConvertible, IComparable <T>, IEquatable <T>, संरचना {...


2

यदि आप .NET 4.0 और बाद में उपयोग कर रहे हैं, तो आप डायनामिक का उपयोग विधि तर्क के रूप में कर सकते हैं और रनटाइम में जांच सकते हैं कि पारित डायनामिक तर्क प्रकार संख्यात्मक / पूर्णांक प्रकार है।

यदि पारित डायनामिक का प्रकार नहीं है सांख्यिक / पूर्णांक प्रकार तो अपवाद फेंक देते हैं।

एक उदाहरण लघु कोड जो विचार को लागू करता है वह कुछ इस प्रकार है:

using System;
public class InvalidArgumentException : Exception
{
    public InvalidArgumentException(string message) : base(message) {}
}
public class InvalidArgumentTypeException : InvalidArgumentException
{
    public InvalidArgumentTypeException(string message) : base(message) {}
}
public class ArgumentTypeNotIntegerException : InvalidArgumentTypeException
{
    public ArgumentTypeNotIntegerException(string message) : base(message) {}
}
public static class Program
{
    private static bool IntegerFunction(dynamic n)
    {
        if (n.GetType() != typeof(Int16) &&
            n.GetType() != typeof(Int32) &&
            n.GetType() != typeof(Int64) &&
            n.GetType() != typeof(UInt16) &&
            n.GetType() != typeof(UInt32) &&
            n.GetType() != typeof(UInt64))
            throw new ArgumentTypeNotIntegerException("argument type is not integer type");
        //code that implements IntegerFunction goes here
    }
    private static void Main()
    {
         Console.WriteLine("{0}",IntegerFunction(0)); //Compiles, no run time error and first line of output buffer is either "True" or "False" depends on the code that implements "Program.IntegerFunction" static method.
         Console.WriteLine("{0}",IntegerFunction("string")); //Also compiles but it is run time error and exception of type "ArgumentTypeNotIntegerException" is thrown here.
         Console.WriteLine("This is the last Console.WriteLine output"); //Never reached and executed due the run time error and the exception thrown on the second line of Program.Main static method.
    }

बेशक यह समाधान केवल रन टाइम में काम करता है लेकिन संकलन समय में कभी नहीं।

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

यह समझ में आता है कि लिपटे हुए डायनामिक हमेशा क्लास / स्ट्रक्चर के निजी सदस्य होते हैं और यह स्ट्रक्चर / क्लास का एकमात्र सदस्य होता है और स्ट्रक्चर / क्लास के एकमात्र सदस्य का नाम "वैल्यू" होता है।

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

यह भी समझ में आता है कि संरचना / वर्ग में विशेष / अद्वितीय रचनाकार है जो डायनेमिक को इस तर्क के रूप में स्वीकार करता है कि यह इनिशियलाइज़ करता है कि यह केवल "मूल्य" नामक निजी डायनामिक मेंबर है लेकिन इस निर्माता का संशोधक निश्चित रूप से निजी है।

एक बार क्लास / स्ट्रक्चर तैयार हो जाने के बाद तर्क के प्रकार को परिभाषित करें कि कौन सी क्लास / स्ट्रक्चर परिभाषित किया गया है।

एक उदाहरण लंबा कोड जो विचार को लागू करता है वह कुछ इस प्रकार है:

using System;
public struct Integer
{
    private dynamic value;
    private Integer(dynamic n) { this.value = n; }
    public Integer(Int16 n) { this.value = n; }
    public Integer(Int32 n) { this.value = n; }
    public Integer(Int64 n) { this.value = n; }
    public Integer(UInt16 n) { this.value = n; }
    public Integer(UInt32 n) { this.value = n; }
    public Integer(UInt64 n) { this.value = n; }
    public Integer(Integer n) { this.value = n.value; }
    public static implicit operator Int16(Integer n) { return n.value; }
    public static implicit operator Int32(Integer n) { return n.value; }
    public static implicit operator Int64(Integer n) { return n.value; }
    public static implicit operator UInt16(Integer n) { return n.value; }
    public static implicit operator UInt32(Integer n) { return n.value; }
    public static implicit operator UInt64(Integer n) { return n.value; }
    public static Integer operator +(Integer x, Int16 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, Int32 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, Int64 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt16 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt32 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt64 y) { return new Integer(x.value + y); }
    public static Integer operator -(Integer x, Int16 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, Int32 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, Int64 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt16 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt32 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt64 y) { return new Integer(x.value - y); }
    public static Integer operator *(Integer x, Int16 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, Int32 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, Int64 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt16 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt32 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt64 y) { return new Integer(x.value * y); }
    public static Integer operator /(Integer x, Int16 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, Int32 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, Int64 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt16 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt32 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt64 y) { return new Integer(x.value / y); }
    public static Integer operator %(Integer x, Int16 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, Int32 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, Int64 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt16 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt32 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt64 y) { return new Integer(x.value % y); }
    public static Integer operator +(Integer x, Integer y) { return new Integer(x.value + y.value); }
    public static Integer operator -(Integer x, Integer y) { return new Integer(x.value - y.value); }
    public static Integer operator *(Integer x, Integer y) { return new Integer(x.value * y.value); }
    public static Integer operator /(Integer x, Integer y) { return new Integer(x.value / y.value); }
    public static Integer operator %(Integer x, Integer y) { return new Integer(x.value % y.value); }
    public static bool operator ==(Integer x, Int16 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int16 y) { return x.value != y; }
    public static bool operator ==(Integer x, Int32 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int32 y) { return x.value != y; }
    public static bool operator ==(Integer x, Int64 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int64 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt16 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt16 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt32 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt32 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt64 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt64 y) { return x.value != y; }
    public static bool operator ==(Integer x, Integer y) { return x.value == y.value; }
    public static bool operator !=(Integer x, Integer y) { return x.value != y.value; }
    public override bool Equals(object obj) { return this == (Integer)obj; }
    public override int GetHashCode() { return this.value.GetHashCode(); }
    public override string ToString() { return this.value.ToString(); }
    public static bool operator >(Integer x, Int16 y) { return x.value > y; }
    public static bool operator <(Integer x, Int16 y) { return x.value < y; }
    public static bool operator >(Integer x, Int32 y) { return x.value > y; }
    public static bool operator <(Integer x, Int32 y) { return x.value < y; }
    public static bool operator >(Integer x, Int64 y) { return x.value > y; }
    public static bool operator <(Integer x, Int64 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt16 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt16 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt32 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt32 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt64 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt64 y) { return x.value < y; }
    public static bool operator >(Integer x, Integer y) { return x.value > y.value; }
    public static bool operator <(Integer x, Integer y) { return x.value < y.value; }
    public static bool operator >=(Integer x, Int16 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int16 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Int32 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int32 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Int64 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int64 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt16 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt16 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt32 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt32 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt64 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt64 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Integer y) { return x.value >= y.value; }
    public static bool operator <=(Integer x, Integer y) { return x.value <= y.value; }
    public static Integer operator +(Int16 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(Int32 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(Int64 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt16 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt32 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt64 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator -(Int16 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(Int32 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(Int64 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt16 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt32 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt64 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator *(Int16 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(Int32 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(Int64 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt16 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt32 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt64 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator /(Int16 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(Int32 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(Int64 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt16 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt32 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt64 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator %(Int16 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(Int32 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(Int64 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt16 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt32 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt64 x, Integer y) { return new Integer(x % y.value); }
    public static bool operator ==(Int16 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int16 x, Integer y) { return x != y.value; }
    public static bool operator ==(Int32 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int32 x, Integer y) { return x != y.value; }
    public static bool operator ==(Int64 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int64 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt16 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt16 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt32 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt32 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt64 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt64 x, Integer y) { return x != y.value; }
    public static bool operator >(Int16 x, Integer y) { return x > y.value; }
    public static bool operator <(Int16 x, Integer y) { return x < y.value; }
    public static bool operator >(Int32 x, Integer y) { return x > y.value; }
    public static bool operator <(Int32 x, Integer y) { return x < y.value; }
    public static bool operator >(Int64 x, Integer y) { return x > y.value; }
    public static bool operator <(Int64 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt16 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt16 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt32 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt32 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt64 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt64 x, Integer y) { return x < y.value; }
    public static bool operator >=(Int16 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int16 x, Integer y) { return x <= y.value; }
    public static bool operator >=(Int32 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int32 x, Integer y) { return x <= y.value; }
    public static bool operator >=(Int64 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int64 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt16 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt16 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt32 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt32 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt64 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt64 x, Integer y) { return x <= y.value; }
}
public static class Program
{
    private static bool IntegerFunction(Integer n)
    {
        //code that implements IntegerFunction goes here
        //note that there is NO code that checks the type of n in rum time, because it is NOT needed anymore 
    }
    private static void Main()
    {
        Console.WriteLine("{0}",IntegerFunction(0)); //compile error: there is no overloaded METHOD for objects of type "int" and no implicit conversion from any object, including "int", to "Integer" is known.
        Console.WriteLine("{0}",IntegerFunction(new Integer(0))); //both compiles and no run time error
        Console.WriteLine("{0}",IntegerFunction("string")); //compile error: there is no overloaded METHOD for objects of type "string" and no implicit conversion from any object, including "string", to "Integer" is known.
        Console.WriteLine("{0}",IntegerFunction(new Integer("string"))); //compile error: there is no overloaded CONSTRUCTOR for objects of type "string"
    }
}

ध्यान दें कि अपने कोड में डायनामिक का उपयोग करने के लिए आपको संदर्भ जोड़ना होगा के लिए Microsoft.CSharp

यदि .NET फ्रेमवर्क का संस्करण 4.0 से कम / कम / कम है और डायनामिक उस संस्करण में अपरिभाषित है, तो आपको इसके बजाय ऑब्जेक्ट का उपयोग करना होगा और पूर्णांक प्रकार के लिए कास्टिंग करना होगा, जो कि परेशानी है, इसलिए मेरा सुझाव है कि आप पर उपयोग करें कम से कम .NET 4.0 या यदि आप ऐसा कर सकते हैं तो आप ऑब्जेक्ट के बजाय डायनेमिक का उपयोग कर सकते हैं ।


2

दुर्भाग्य से .NET नेटिव रूप से ऐसा करने का एक तरीका प्रदान नहीं करता है।

इस समस्या को हल करने के लिए मैंने OSS लाइब्रेरी जेनुमेरिक्स बनाया निर्माण किया जो निम्नलिखित अंतर्निहित अंकीय प्रकारों के लिए अधिकांश मानक संख्यात्मक संचालन प्रदान करता है और अन्य संख्यात्मक प्रकारों के लिए समर्थन जोड़ने की क्षमता के साथ उनके अशक्त समकक्ष हैं।

sbyte, byte, short, ushort, int, uint, long, ulong, float, double, decimal, औरBigInteger

प्रदर्शन एक संख्यात्मक प्रकार के विशिष्ट समाधान के बराबर है जो आपको कुशल सामान्य संख्यात्मक एल्गोरिदम बनाने की अनुमति देता है।

यहाँ कोड उपयोग का एक उदाहरण है।

public static T Sum(T[] items)
{
    T sum = Number.Zero<T>();
    foreach (T item in items)
    {
        sum = Number.Add(sum, item);
    }
    return sum;
}
public static T SumAlt(T[] items)
{
    // implicit conversion to Number<T>
    Number<T> sum = Number.Zero<T>();
    foreach (T item in items)
    {
        // operator support
        sum += item;
    }
    // implicit conversion to T
    return sum;
}

1

अभ्यास की बात क्या है?

जैसा कि लोगों ने पहले ही बताया, आप एक गैर-जेनेरिक फ़ंक्शन सबसे बड़ी वस्तु ले सकते हैं, और कंपाइलर स्वचालित रूप से आपके लिए छोटे चींटियों को बदल देगा।

static bool IntegerFunction(Int64 value) { }

यदि आपका कार्य प्रदर्शन-महत्वपूर्ण पथ (बहुत ही असंभावित, IMO) पर है, तो आप सभी आवश्यक कार्यों के लिए ओवरलोड प्रदान कर सकते हैं।

static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }

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

1

मैं एक जेनेरिक का उपयोग करूंगा जिसे आप बाहरी रूप से संभाल सकते हैं ...

/// <summary>
/// Generic object copy of the same type
/// </summary>
/// <typeparam name="T">The type of object to copy</typeparam>
/// <param name="ObjectSource">The source object to copy</param>
public T CopyObject<T>(T ObjectSource)
{
    T NewObject = System.Activator.CreateInstance<T>();

    foreach (PropertyInfo p in ObjectSource.GetType().GetProperties())
        NewObject.GetType().GetProperty(p.Name).SetValue(NewObject, p.GetValue(ObjectSource, null), null);

    return NewObject;
}

1

जब मैंने सामान्य प्रकारों के लिए ऑपरेटरों को अधिभारित करने की कोशिश की तो इस सीमा ने मुझे प्रभावित किया; चूँकि वहाँ "इन्यूमेरिक" बाधा नहीं थी, और अन्य कारणों से बीवी के लिए स्टैकओवरफ़्लो पर अच्छे लोग प्रदान करने के लिए खुश हैं, संचालन सामान्य प्रकारों पर परिभाषित नहीं किया जा सकता है।

मुझे कुछ ऐसा चाहिए था

public struct Foo<T>
{
    public T Value{ get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + RHS.Value; };
    }
}

मैंने .net4 डायनेमिक रनटाइम टाइपिंग का उपयोग करते हुए इस मुद्दे पर काम किया है।

public struct Foo<T>
{
    public T Value { get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + (dynamic)RHS.Value };
    }
}

उपयोग करने के बारे में दो बातें dynamicहैं

  1. प्रदर्शन। सभी मूल्य प्रकार बॉक्सिंग हो जाते हैं।
  2. रनटाइम त्रुटियों। आप संकलक को "हरा" देते हैं, लेकिन प्रकार की सुरक्षा खो देते हैं। यदि सामान्य प्रकार में ऑपरेटर परिभाषित नहीं है, तो निष्पादन के दौरान एक अपवाद फेंक दिया जाएगा।

1

.NET सांख्यिक आदिम प्रकार किसी भी सामान्य इंटरफ़ेस को साझा नहीं करते हैं जो उन्हें गणना के लिए उपयोग करने की अनुमति देगा। यह अपने स्वयं के इंटरफेस (जैसे परिभाषित करने के लिए संभव हो जाएगा ISignedWholeNumber) जो इस प्रकार की गतिविधियों को अंजाम होता है, संरचनाओं जो एक एकल शामिल परिभाषित Int16, Int32आदि और उन इंटरफेस को लागू करें, और उसके तरीकों जो करने के लिए विवश सामान्य प्रकार स्वीकार किया है ISignedWholeNumber, लेकिन संख्यात्मक मान परिवर्तित करने के लिए हो रही आपकी संरचना के प्रकारों में एक उपद्रव होगा।

एक वैकल्पिक दृष्टिकोण स्थिर वर्ग को परिभाषित करने के लिए किया जाएगा Int64Converter<T>एक स्थिर संपत्ति के साथ bool Available {get;};और के लिए स्थिर प्रतिनिधियों Int64 GetInt64(T value), T FromInt64(Int64 value), bool TryStoreInt64(Int64 value, ref T dest)। क्लास कंस्ट्रक्टर को ज्ञात प्रकारों के लिए प्रतिनिधियों को लोड करने के लिए हार्ड-कोडेड का उपयोग किया जा सकता है, और संभवतः परीक्षण करने के लिए प्रतिबिंब का उपयोग करें कि क्या Tउचित नाम और हस्ताक्षर के साथ प्रकार के तरीकों को लागू करता है (मामले में यह एक संरचना की तरह कुछ है जिसमें Int64एक संख्या शामिल है और एक संख्या का प्रतिनिधित्व करता है, लेकिन एक कस्टम ToString()विधि)। यह दृष्टिकोण कंपाइल-टाइम टाइप-चेकिंग से जुड़े फायदे खो देगा, लेकिन फिर भी बॉक्सिंग संचालन से बचने के लिए प्रबंधन करेगा और प्रत्येक प्रकार को केवल एक बार "चेक" करना होगा। उसके बाद, उस प्रकार से जुड़े कार्यों को एक प्रतिनिधि प्रेषण के साथ बदल दिया जाएगा।


@KenKin: IConvertible एक ऐसा साधन प्रदान करता है जिसके द्वारा किसी भी पूर्णांक को किसी पूर्णांक प्रकार में जोड़ा जा सकता है जैसे कि Int64परिणाम के लिए, लेकिन ऐसा साधन प्रदान नहीं करता है, जिसके द्वारा उदाहरण के लिए मनमाने प्रकार के पूर्णांक को उसी प्रकार के किसी अन्य पूर्णांक को प्राप्त करने के लिए बढ़ाया जा सके ।
सुपरकैट

1

मेरे पास एक समान स्थिति थी जहां मुझे संख्यात्मक प्रकार और तारों को संभालने की आवश्यकता थी; थोड़ा विचित्र मिश्रण लगता है, लेकिन आप वहां जाते हैं।

फिर से, कई लोगों की तरह मैंने बाधाओं को देखा और इंटरफेस का एक गुच्छा लेकर आया, जिसे इसका समर्थन करना था। हालाँकि, क) यह 100% जलप्रपात और ख) नहीं था, किसी को भी बाधाओं की इस लंबी सूची को देखते हुए तुरंत बहुत उलझन होगी।

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

public static string DoSomething(this int input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this decimal input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this double input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this string input, ...) => DoSomethingHelper(input, ...);

private static string DoSomethingHelper<T>(this T input, ....)
{
    // complex logic
}

0

यदि आप चाहते हैं कि सभी एक संख्यात्मक प्रकार का उपयोग करें , तो आप C ++ में एक उपनाम के समान कुछ बनाने पर विचार कर सकते हैं using

तो इसके बजाय बहुत ही सामान्य है

T ComputeSomething<T>(T value1, T value2) where T : INumeric { ... }

आप ऐसा कर सकते थे

using MyNumType = System.Double;
T ComputeSomething<MyNumType>(MyNumType value1, MyNumType value2) { ... }

यही कारण है कि आप आसानी से से जाना अनुमति दे सकते हैं doubleकरने के लिए intया दूसरों अगर जरूरत है, लेकिन आप का उपयोग करने के लिए सक्षम नहीं होगा ComputeSomethingके साथ doubleऔर intएक ही कार्यक्रम में।

लेकिन सभी की जगह क्यों नहीं double करने के लिए intतो? क्योंकि आपकी विधि doubleइनपुट का उपयोग करना चाहती है doubleया कर सकती है int। उपनाम आपको यह जानने की अनुमति देता है कि कौन सा चर गतिशील प्रकार का उपयोग करता है ।


0

विषय पुराना है, लेकिन भविष्य के पाठकों के लिए:

यह सुविधा कसकर संबंधित है Discriminated Unionsजो C # में अब तक लागू नहीं हुई है। मुझे इसका मुद्दा यहाँ मिला:

https://github.com/dotnet/csharplang/issues/113

यह मुद्दा अभी भी खुला है और इसके लिए फीचर की योजना बनाई गई है C# 10

इसलिए अभी भी हमें थोड़ा और इंतजार करना होगा, लेकिन रिलीज होने के बाद आप इसे इस तरह से कर सकते हैं:

static bool IntegerFunction<T>(T value) where T : Int16 | Int32 | Int64 | ...

-11

मुझे लगता है कि आप गलतफहमी की जेनरिक हैं। यदि आप जो ऑपरेशन करने की कोशिश कर रहे हैं वह केवल विशिष्ट डेटा प्रकारों के लिए अच्छा है तो आप कुछ "सामान्य" नहीं कर रहे हैं।

इसके अलावा, चूंकि आप केवल फ़ंक्शन को अंतर डेटा प्रकारों पर काम करने की अनुमति देना चाहते हैं, तो आपको प्रत्येक विशिष्ट आकार के लिए एक अलग फ़ंक्शन की आवश्यकता नहीं होनी चाहिए। बस सबसे बड़े विशिष्ट प्रकार में एक पैरामीटर लेने से प्रोग्राम को छोटे डेटा प्रकारों को स्वचालित रूप से इसे करने की अनुमति मिलेगी। (यानी एक Int16 पास जब कॉल करने के लिए Int64 में ऑटो-कन्वर्ट होगा)।

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

अन्य सभी को विफल करते हुए, टाइप ऑब्जेक्ट का एक पैरामीटर इस्तेमाल किया जा सकता है और फिर आपको पैरामीटर के प्रकार की जांच करनी होगी और उचित कार्रवाई करनी होगी या एक अपवाद फेंकना होगा।


10
एक वर्ग हिस्टोग्राम <टी> पर विचार करें। यह समझ में आता है कि इसे एक जेनेरिक पैरामीटर लेने दिया जाए, इसलिए कंपाइलर इसे बाइट्स, इन्टल्स, डबल्स, दशमलव, बिगइंट के लिए ऑप्टिमाइज़ कर सकता है, ... लेकिन इसके साथ ही आपको इसे रोकने की आवश्यकता है कि आप एक, कह सकते हैं, हिस्टोग्राम <आशसेट >, क्योंकि - ट्रॉन के साथ बोलना - यह गणना नहीं करता है। (वस्तुतः :))
सूर्योदय

15
आप वही हैं जो जेनरिक को गलत समझते हैं। मेटाप्रोग्रामिंग केवल उन मानों पर काम नहीं कर रहा है जो किसी भी संभावित प्रकार के हो सकते हैं , यह उन प्रकारों के संचालन के लिए है जो विभिन्न बाधाओं को फिट करते हैं ।
जिम बेल्टर 19'13
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.