c# जेनेरिक संरचना के रूप में मजबूत टाइप गाइड




generics struct (3)

हम वही करते हैं, यह बहुत अच्छा काम करता है।

हां, यह बहुत कॉपी और पेस्ट है, लेकिन यह वही है जो कोड-पीढ़ी के लिए है।

Visual Studio में, आप इसके लिए T4 टेम्प्लेट का उपयोग कर सकते हैं। आप मूल रूप से अपनी कक्षा को एक बार लिखते हैं और फिर एक टेम्पलेट रखते हैं जहाँ आप कहते हैं कि "मुझे इस वर्ग के लिए ऐप, भुगतान, खाता, ..." चाहिए और विज़ुअल स्टूडियो आपको प्रत्येक के लिए एक स्रोत कोड फ़ाइल उत्पन्न करेगा।

इस तरह से आपके पास एक एकल स्रोत (T4 टेम्प्लेट) है जहां आप अपनी कक्षाओं में बग ढूंढने पर परिवर्तन कर सकते हैं और यह आपके सभी पहचानकर्ताओं को आपके बारे में विचार किए बिना प्रचारित करेगा।

मैं पहले से ही कोड की तरह ही दो बार बग बनाता हूं:

void Foo(Guid appId, Guid accountId, Guid paymentId, Guid whateverId)
{
...
}

Guid appId = ....;
Guid accountId = ...;
Guid paymentId = ...;
Guid whateverId =....;

//BUG - parameters are swapped - but compiler compiles it
Foo(appId, paymentId, accountId, whateverId);

ठीक है, मैं इन बग्स को रोकना चाहता हूं, इसलिए मैंने दृढ़ता से टाइप किए गए GUID बनाए:

[ImmutableObject(true)]
public struct AppId
{
    private readonly Guid _value;

    public AppId(string value)
    {            
        var val = Guid.Parse(value);
        CheckValue(val);
        _value = val;
    }      

    public AppId(Guid value)
    {
        CheckValue(value);
        _value = value;           
    }

    private static void CheckValue(Guid value)
    {
        if(value == Guid.Empty)
            throw new ArgumentException("Guid value cannot be empty", nameof(value));
    }

    public override string ToString()
    {
        return _value.ToString();
    }
}

और PaymentId के लिए एक और एक:

[ImmutableObject(true)]
public struct PaymentId
{
    private readonly Guid _value;

    public PaymentId(string value)
    {            
        var val = Guid.Parse(value);
        CheckValue(val);
        _value = val;
    }      

    public PaymentId(Guid value)
    {
        CheckValue(value);
        _value = value;           
    }

    private static void CheckValue(Guid value)
    {
        if(value == Guid.Empty)
            throw new ArgumentException("Guid value cannot be empty", nameof(value));
    }

    public override string ToString()
    {
        return _value.ToString();
    }
}

ये संरचनाएं लगभग समान हैं, कोड का बहुत दोहराव है। है ना?

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

क्या आपके पास कुछ विचार है जो बिना डुप्लिकेट कोड के संरचना का उपयोग कैसे करें?


आप एक अलग प्रोग्रामिंग भाषा के साथ उपवर्ग का उपयोग करने में सक्षम हो सकते हैं।


सबसे पहले, यह एक बहुत अच्छा विचार है। एक तरफ संक्षिप्त:

मैं चाहता हूं कि C # ने पूर्णांक, स्ट्रिंग्स, आईडी और इतने पर सस्ते टाइप के रैपर बनाना आसान बना दिया। हम बहुत "स्ट्रिंग खुश" और प्रोग्रामर के रूप में "पूर्णांक खुश" हैं; बहुत सी चीजों को तार और पूर्णांक के रूप में दर्शाया जाता है जो कि टाइप सिस्टम में ट्रैक की गई अधिक जानकारी हो सकती है; हम ग्राहकों के पते पर ग्राहक के नाम निर्दिष्ट नहीं करना चाहते हैं। कुछ समय पहले मैंने OCaml में एक वर्चुअल मशीन लिखने के बारे में ब्लॉग पोस्ट की श्रृंखला (कभी भी समाप्त नहीं हुई!) लिखी थी, और मैंने जो सबसे अच्छा काम किया था, उसमें से प्रत्येक पूर्णांक को वर्चुअल मशीन में एक प्रकार से लपेटा गया था जो इसके उद्देश्य को इंगित करता है। कि इतने सारे कीड़े रोका! OCaml छोटे आवरण प्रकार बनाने के लिए बहुत आसान बनाता है; C # नहीं करता है।

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

यदि आप कॉपी-पेस्ट किए गए कोड से बचना चाहते हैं, तो मैं इस तरह से जेनरिक का उपयोग करने का सुझाव दूंगा:

struct App {}
struct Payment {}

public struct Id<T>
{
    private readonly Guid _value;
    public Id(string value)
    {            
        var val = Guid.Parse(value);
        CheckValue(val);
        _value = val;
    }

    public Id(Guid value)
    {
        CheckValue(value);
        _value = value;           
    }

    private static void CheckValue(Guid value)
    {
        if(value == Guid.Empty)
            throw new ArgumentException("Guid value cannot be empty", nameof(value));
    }

    public override string ToString()
    {
        return _value.ToString();
    }
}

और अब आप कर रहे हैं। आपके पास AppId और PaymentId बजाय Id<App> और Id<Payment> PaymentId , लेकिन फिर भी आप Id<App> Id<Payment> या PaymentId असाइन नहीं कर सकते।

इसके अलावा, यदि आप AppId और PaymentId का उपयोग करना पसंद करते AppId तो अपनी फ़ाइल के शीर्ष पर आप कह सकते हैं

using AppId = MyNamespace.Whatever.Id<MyNamespace.Whatever.App>

और इसी तरह।

तीसरा, आपको अपने प्रकार में कुछ और विशेषताओं की आवश्यकता होगी; मुझे लगता है कि यह अभी तक नहीं किया गया है। उदाहरण के लिए, आपको संभवतः समानता की आवश्यकता होगी, ताकि आप यह जांच सकें कि क्या दो आईडी समान हैं।

चौथा, इस बात से अवगत रहें कि default(Id<App>) अभी भी आपको एक "खाली गाइड" पहचानकर्ता देता है, इसलिए इसे रोकने का आपका प्रयास वास्तव में काम नहीं करता है; यह अभी भी एक बनाना संभव होगा। वहाँ वास्तव में एक अच्छा तरीका नहीं है।





guid