रिपशर ने चेतावनी दी: "सामान्य प्रकार में स्थिर क्षेत्र"


261
public class EnumRouteConstraint<T> : IRouteConstraint
    where T : struct
{
    private static readonly Lazy<HashSet<string>> _enumNames; // <--

    static EnumRouteConstraint()
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException(
                Resources.Error.EnumRouteConstraint.FormatWith(typeof(T).FullName));
        }

        string[] names = Enum.GetNames(typeof(T));
        _enumNames = new Lazy<HashSet<string>>(() => new HashSet<string>
        (
            names.Select(name => name), StringComparer.InvariantCultureIgnoreCase
        ));
    }

    public bool Match(HttpContextBase httpContext, Route route, 
                        string parameterName, RouteValueDictionary values, 
                        RouteDirection routeDirection)
    {
        bool match = _enumNames.Value.Contains(values[parameterName].ToString());
        return match;
    }
}

क्या यह गलत है? मुझे लगता है कि यह वास्तव में static readonlyसंभव है EnumRouteConstraint<T>कि मैं उदाहरण के लिए प्रत्येक के लिए एक क्षेत्र है ।


कभी इसकी विशेषता, तो कभी झुंझलाहट। मैं चाहता था कि C # के पास उनके भेद करने के लिए कुछ कीवर्ड हों
nawfal

जवाबों:


468

जेनेरिक प्रकार में एक स्थिर फ़ील्ड होना ठीक है, इसलिए जब तक आप जानते हैं कि आपको वास्तव में टाइप तर्क के संयोजन के लिए एक फ़ील्ड मिलेगा। मेरा अनुमान है कि R # आपको चेतावनी दे रहा है यदि आप उस बारे में नहीं जानते हैं।

यहाँ इसका एक उदाहरण दिया गया है:

using System;

public class Generic<T>
{
    // Of course we wouldn't normally have public fields, but...
    public static int Foo;
}

public class Test
{
    public static void Main()
    {
        Generic<string>.Foo = 20;
        Generic<object>.Foo = 10;
        Console.WriteLine(Generic<string>.Foo); // 20
    }
}

जैसा कि आप देख सकते हैं, Generic<string>.Fooएक अलग क्षेत्र है Generic<object>.Foo- वे अलग-अलग मान रखते हैं।


क्या यह तब भी सच है जब जेनेरिक कक्षाएं एक गैर-जेनेरिक वर्ग से विरासत में मिलती हैं जिसमें स्थिर प्रकार होते हैं। उदाहरण के लिए यदि मैं class BaseFooएक स्थिर सदस्य बनाता हूं , तो इससे प्राप्त class Foo<T>: BaseFooसभी Foo<T>वर्ग समान स्थिर सदस्य मान साझा करेंगे ?
bikeman868

2
यहाँ मेरी अपनी टिप्पणी का उत्तर देना, लेकिन हाँ, सभी Foo <T> का समान स्थिर मूल्य होगा यदि यह गैर-जेनेरिक आधार वर्ग में समाहित है। देखें dotnetfiddle.net/Wz75ya
bikeman868

147

से जेटब्रेन्स विकि :

अधिकांश मामलों में, सामान्य प्रकार में स्थिर फ़ील्ड का होना त्रुटि का संकेत है। इसका कारण यह है कि एक सामान्य प्रकार में एक स्थिर क्षेत्र को विभिन्न करीबी प्रकारों के उदाहरणों के बीच साझा नहीं किया जाएगा । इसका मतलब है कि एक सामान्य वर्ग के लिए C<T>जिसमें एक स्थिर क्षेत्र है X, के मूल्य C<int>.Xऔर C<string>.X पूरी तरह से अलग, स्वतंत्र मूल्य हैं।

दुर्लभ मामलों में जब आप करते हैं की जरूरत है 'विशेष' स्थिर क्षेत्रों, चेतावनी को दबाने के लिए स्वतंत्र लग रहा है।

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


13
जब आप एक सामान्य प्रकार को नियोजित करते हैं, तो तकनीकी रूप से आप प्रत्येक सामान्य प्रकार के लिए एक अलग और अलग वर्ग के साथ समाप्त होते हैं, जिसकी आप मेजबानी कर रहे हैं। दो अलग-अलग, गैर-सामान्य वर्गों की घोषणा करते समय, आप उन दोनों के बीच स्थिर चर साझा करने की उम्मीद नहीं करेंगे, इसलिए जेनरिक को अलग क्यों होना चाहिए? एकमात्र तरीका यह दुर्लभ माना जा सकता है, यदि अधिकांश डेवलपर्स समझ नहीं पाते हैं कि वे सामान्य कक्षाएं बनाते समय क्या कर रहे हैं।
Syndog

2
@ सामान्य वर्ग के भीतर स्टैटिक्स का वर्णित व्यवहार मेरे लिए ठीक और समझ में आता है। लेकिन मुझे लगता है कि उन चेतावनियों के पीछे का कारण यह है कि हर टीम में केवल अनुभवी और केंद्रित डेवलपर्स नहीं होते हैं। डेवलपर की योग्यता के कारण सही कोड त्रुटि-प्रवण हो जाता है।
स्टास इवानोव

लेकिन क्या होगा अगर मैं इन स्थिर क्षेत्रों को रखने के लिए एक गैर-सामान्य आधार वर्ग नहीं बनाना चाहता। क्या मैं सिर्फ इस मामले में चेतावनी को दबा सकता हूं?
टॉम लिंट

@TomLint यदि आप जानते हैं कि आप क्या कर रहे हैं तो चेतावनियों को दबाना वास्तव में करने की चीज है।
आकाशवाणी २ A

65

यह आवश्यक रूप से त्रुटि नहीं है - यह आपको C # जेनरिक की संभावित गलतफहमी के बारे में चेतावनी दे रहा है ।

जेनेरिक क्या करते हैं, यह याद रखने का सबसे आसान तरीका निम्नलिखित है: क्लासिक्स बनाने के लिए जेनेरिक "ब्लूप्रिंट" होते हैं, बहुत तरह की कक्षाएं ऑब्जेक्ट बनाने के लिए "ब्लूप्रिंट" होती हैं। (ठीक है, हालांकि यह एक सरलीकरण है। आप विधि जेनरिक का भी उपयोग कर सकते हैं।)

इस दृष्टिकोण से MyClassRecipe<T>एक वर्ग नहीं है - यह एक नुस्खा है, एक खाका है, जो आपकी कक्षा जैसा दिखेगा। एक बार जब आप कुछ ठोस के साथ टी का विकल्प देते हैं, तो इंट, स्ट्रिंग, आदि कहते हैं, आपको एक वर्ग मिलता है। आपके नए बनाए गए वर्ग (जैसा कि किसी भी अन्य वर्ग में) में घोषित किया गया है और यहाँ किसी भी त्रुटि का कोई संकेत नहीं है, एक स्थिर सदस्य (क्षेत्र, संपत्ति, विधि) होना पूरी तरह से कानूनी है। यह कुछ हद तक संदेहास्पद होगा, पहली नजर में, यदि आप static MyStaticProperty<T> Property { get; set; }अपने वर्ग खाका के भीतर घोषित करते हैं, लेकिन यह कानूनी है। आपकी संपत्ति का मानकीकरण किया जाएगा, या अस्थायी, साथ ही साथ।

वीबी स्टैटिक्स में कोई आश्चर्य नहीं कहा जाता है shared। इस मामले में, आपको इस बात की जानकारी होनी चाहिए कि ऐसे "साझा" सदस्य केवल एक ही सटीक वर्ग के उदाहरणों के बीच साझा किए जाते हैं, न कि <T>किसी अन्य चीज़ के साथ प्रतिस्थापित करके निर्मित अलग-अलग वर्गों के बीच ।


1
मुझे लगता है कि C ++ का नाम सभी को स्पष्ट करता है। C ++ में उन्हें टेम्प्लेट्स कहा जाता है, जो कि वे हैं, ठोस कक्षाओं के लिए टेम्प्लेट।
माइकल ब्राउन

8

यहां पहले से ही कई अच्छे जवाब हैं, जो चेतावनी और इसके कारण को स्पष्ट करते हैं। इनमें से कई राज्य एक सामान्य प्रकार में एक स्थिर क्षेत्र होने की तरह आम तौर पर एक गलती है

मैंने सोचा कि मैं एक उदाहरण जोड़ूंगा कि यह सुविधा कैसे उपयोगी हो सकती है, यानी एक ऐसा मामला जहां आर # दबाने से समझ में आता है।

कल्पना कीजिए कि आपके पास इकाई-वर्गों का एक सेट है जिसे आप क्रमबद्ध करना चाहते हैं, एक्सएमएल से कहें। आप इस का उपयोग करने के लिए एक serializer बना सकते हैं new XmlSerializerFactory().CreateSerializer(typeof(SomeClass)), लेकिन फिर आपको प्रत्येक प्रकार के लिए एक अलग serializer बनाना होगा। जेनेरिक का उपयोग करते हुए, आप इसे निम्नलिखित के साथ बदल सकते हैं, जिसे आप एक सामान्य वर्ग में रख सकते हैं, जो निकाय इससे प्राप्त कर सकते हैं:

new XmlSerializerFactory().CreateSerializer(typeof(T))

चूँकि आपका संभवत: हर बार आपको किसी विशेष प्रकार की आवृत्ति को क्रमबद्ध करने के लिए एक नया धारावाहिक बनाने की ज़रूरत नहीं है, आप इसे जोड़ सकते हैं:

public class SerializableEntity<T>
{
    // ReSharper disable once StaticMemberInGenericType
    private static XmlSerializer _typeSpecificSerializer;

    private static XmlSerializer TypeSpecificSerializer
    {
        get
        {
            // Only create an instance the first time. In practice, 
            // that will mean once for each variation of T that is used,
            // as each will cause a new class to be created.
            if ((_typeSpecificSerializer == null))
            {
                _typeSpecificSerializer = 
                    new XmlSerializerFactory().CreateSerializer(typeof(T));
            }

            return _typeSpecificSerializer;
        }
    }

    public virtual string Serialize()
    {
        // .... prepare for serializing...

        // Access _typeSpecificSerializer via the property, 
        // and call the Serialize method, which depends on 
        // the specific type T of "this":
        TypeSpecificSerializer.Serialize(xmlWriter, this);
     }
}

यदि यह वर्ग सामान्य नहीं था, तो कक्षा का प्रत्येक उदाहरण उसी का उपयोग करेगा _typeSpecificSerializer

हालांकि, यह सामान्य है, लेकिन इसके लिए एक ही प्रकार के उदाहरणों का एक सेट (जो उस विशिष्ट प्रकार के लिए बनाया गया है) Tका एक उदाहरण साझा _typeSpecificSerializerकरेगा, जबकि विभिन्न प्रकार के लिए Tउदाहरणों के विभिन्न उदाहरणों का उपयोग करेगा _typeSpecificSerializer

एक उदाहरण

दो वर्गों का विस्तार किया है SerializableEntity<T>:

// Note that T is MyFirstEntity
public class MyFirstEntity : SerializableEntity<MyFirstEntity>
{
    public string SomeValue { get; set; }
}

// Note that T is OtherEntity
public class OtherEntity : SerializableEntity<OtherEntity >
{
    public int OtherValue { get; set; }
}

... चलो उनका उपयोग करें:

var firstInst = new MyFirstEntity{ SomeValue = "Foo" };
var secondInst = new MyFirstEntity{ SomeValue = "Bar" };

var thirdInst = new OtherEntity { OtherValue = 123 };
var fourthInst = new OtherEntity { OtherValue = 456 };

var xmlData1 = firstInst.Serialize();
var xmlData2 = secondInst.Serialize();
var xmlData3 = thirdInst.Serialize();
var xmlData4 = fourthInst.Serialize();

इस मामले में, हुड के तहत, firstInstऔर secondInstएक ही वर्ग (अर्थात् SerializableEntity<MyFirstEntity>) के उदाहरण होंगे , और जैसे, वे एक उदाहरण साझा करेंगे _typeSpecificSerializer

thirdInstऔर fourthInstएक अलग वर्ग (के उदाहरण हैं SerializableEntity<OtherEntity>), और इसलिए एक उदाहरण साझा करेंगे की _typeSpecificSerializerवह यह है कि विभिन्न अन्य दो से।

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


स्टेटिक इनिशियलाइज़ेशन के नियमों के कारण (स्टैटिक इनिशियलाइज़र को तब तक नहीं बुलाया जाता है जब तक कि क्लास को पहली बार संदर्भित नहीं किया जाता है) आप गेट्टर में चेक को वापस कर सकते हैं और इसे स्टैटिक इंस्टेंस डिक्लेरेशन में इनिशियलाइज़ कर सकते हैं।
माइकल ब्राउन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.