अशक्त सब कुछ के लिए सी # सामान्य प्रकार की बाधा


111

तो मेरे पास यह वर्ग है:

public class Foo<T> where T : ???
{
    private T item;

    public bool IsNull()
    {
        return item == null;
    }

}

अब मुझे एक प्रकार की बाधा की तलाश है जो मुझे टाइप पैरामीटर के रूप में सब कुछ उपयोग करने की अनुमति देता है null। इसका मतलब है कि सभी संदर्भ प्रकार, साथ ही सभी Nullable( T?) प्रकार:

Foo<String> ... = ...
Foo<int?> ... = ...

संभव होना चाहिए।

का प्रयोग classके रूप में प्रकार बाधा सिर्फ मेरे संदर्भ प्रकार का उपयोग करने की अनुमति देता है।

अतिरिक्त जानकारी: मैं एक पाइप और फिल्टर एप्लीकेशन लिख रहा हूं, और एक nullसंदर्भ का उपयोग अंतिम आइटम के रूप में करना चाहता हूं जो पाइप लाइन में गुजरता है, ताकि हर फिल्टर अच्छी तरह से बंद हो सके, सफाई करें, आदि ...


1
@Tim जो Nullables के लिए अनुमति नहीं देता है
Rik

यह लिंक आपकी मदद कर सकता है: social.msdn.microsoft.com/Forums/en-US/…
Réda Matar

2
सीधे तौर पर ऐसा करना संभव नहीं है। शायद आप हमें अपने परिदृश्य के बारे में अधिक बता सकते हैं? या शायद आप IFoo<T>काम करने के प्रकार के रूप में उपयोग कर सकते हैं और एक कारखाने विधि के माध्यम से उदाहरण बना सकते हैं? यह काम करने के लिए बनाया जा सकता है।
जॉन

मुझे यकीन नहीं है कि आप क्यों चाहते हैं या इस तरह से कुछ करने की आवश्यकता होगी। यदि आपका एकमात्र इरादा "if x == null" को यदि x.IsNull () में बदलना है, तो यह 99.99% डेवलपर्स के लिए बेकार और अनपेक्षित लगता है, जो पूर्व सिंटैक्स के लिए उपयोग किए जाते हैं। कंपाइलर आपको नहीं होने देगा। " अगर (int) x == अशक्त "वैसे भी, तो आप पहले से ही कवर कर रहे हैं।
RJ Lohan

1
यह बहुत व्यापक रूप से एसओ पर चर्चा की है। stackoverflow.com/questions/209160/… और stackoverflow.com/questions/13794554/…
मैक्सिम गेर्शकोविच

जवाबों:


22

यदि आप संकलन-समय की जांच के बजाय फू के निर्माता में रनटाइम चेक करने के लिए तैयार हैं, तो आप जांच कर सकते हैं कि क्या प्रकार एक संदर्भ या अशक्त प्रकार नहीं है, और यदि ऐसा है तो अपवाद फेंक दें।

मुझे एहसास है कि केवल एक रनटाइम चेक अस्वीकार्य हो सकता है, लेकिन सिर्फ मामले में:

public class Foo<T>
{
    private T item;

    public Foo()
    {
        var type = typeof(T);

        if (Nullable.GetUnderlyingType(type) != null)
            return;

        if (type.IsClass)
            return;

        throw new InvalidOperationException("Type is not nullable or reference type.");
    }

    public bool IsNull()
    {
        return item == null;
    }
}

फिर निम्न कोड संकलित करता है, लेकिन अंतिम एक ( foo3) कंस्ट्रक्टर में एक अपवाद फेंकता है:

var foo1 = new Foo<int?>();
Console.WriteLine(foo1.IsNull());

var foo2 = new Foo<string>();
Console.WriteLine(foo2.IsNull());

var foo3= new Foo<int>();  // THROWS
Console.WriteLine(foo3.IsNull());

31
यदि आप ऐसा करने जा रहे हैं, तो सुनिश्चित करें कि आप स्थिर कंस्ट्रक्टर में चेक करते हैं , अन्यथा आप अपने सामान्य वर्ग (अनावश्यक रूप से) के हर उदाहरण के निर्माण को धीमा कर देंगे
Eamon Nerbonne

2
@EamonNerbonne आपको स्थैतिक निर्माणकर्ताओं से अपवाद नहीं उठाना चाहिए: msdn.microsoft.com/en-us/library/bb386039.aspx
मैथ्यू वाटसन

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

संयोग से, मुझे नहीं लगता कि आपको ऐसा करने की कोशिश करनी चाहिए। ज्यादातर परिस्थितियों में मैं इसे केवल सी # लिमिटेशन के रूप में स्वीकार करना पसंद करूंगा, बजाय इसके कि कोशिश करें और एक टपका हुआ, विफलता-विफलता के साथ काम करें। उदाहरण के लिए एक अलग समाधान केवल कक्षाओं की आवश्यकता हो सकती है, या बस संरचना की आवश्यकता होती है (और स्पष्ट रूप से उन्हें अशक्त बनाने के लिए) - या दोनों करें और दो संस्करण हैं। यह इस समाधान की आलोचना नहीं है; यह सिर्फ इतना है कि इस समस्या को अच्छी तरह से हल नहीं किया जा सकता है - जब तक कि, आप एक कस्टम रोस्लिन विश्लेषक लिखने के लिए तैयार नहीं हैं।
Eamon Nerbonne

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

20

मुझे नहीं पता कि जेनेरिक में OR के बराबर कैसे लागू किया जाए । हालाँकि, मैं अशक्त प्रकारों के लिए अशक्त और संरचनाओं के लिए 0 मान बनाने के लिए डिफ़ॉल्ट कुंजी शब्द का उपयोग करने का प्रस्ताव कर सकता हूं :

public class Foo<T>
{
    private T item;

    public bool IsNullOrDefault()
    {
        return Equals(item, default(T));
    }
}

आप Nullable का संस्करण भी लागू कर सकते हैं:

class MyNullable<T> where T : struct
{
    public T Value { get; set; }

    public static implicit operator T(MyNullable<T> value)
    {
        return value != null ? value.Value : default(T);
    }

    public static implicit operator MyNullable<T>(T value)
    {
        return new MyNullable<T> { Value = value };
    }
}

class Foo<T> where T : class
{
    public T Item { get; set; }

    public bool IsNull()
    {
        return Item == null;
    }
}

उदाहरण:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(new Foo<MyNullable<int>>().IsNull()); // true
        Console.WriteLine(new Foo<MyNullable<int>> {Item = 3}.IsNull()); // false
        Console.WriteLine(new Foo<object>().IsNull()); // true
        Console.WriteLine(new Foo<object> {Item = new object()}.IsNull()); // false

        var foo5 = new Foo<MyNullable<int>>();
        int integer = foo5.Item;
        Console.WriteLine(integer); // 0

        var foo6 = new Foo<MyNullable<double>>();
        double real = foo6.Item;
        Console.WriteLine(real); // 0

        var foo7 = new Foo<MyNullable<double>>();
        foo7.Item = null;
        Console.WriteLine(foo7.Item); // 0
        Console.WriteLine(foo7.IsNull()); // true
        foo7.Item = 3.5;
        Console.WriteLine(foo7.Item); // 3.5
        Console.WriteLine(foo7.IsNull()); // false

        // var foo5 = new Foo<int>(); // Not compile
    }
}

फ्रेम में मूल Nullable <T> एक संरचना है, न कि एक वर्ग। मुझे नहीं लगता कि एक संदर्भ प्रकार के आवरण को बनाने के लिए यह एक अच्छा विचार है जो एक मूल्य प्रकार की नकल करेगा।
नियाल कनॉटघटन

1
डिफ़ॉल्ट का उपयोग करने वाला पहला सुझाव एकदम सही है! अब मेरा जेनेरिक प्रकार वाला टेम्पलेट लौटाया जा सकता है जो वस्तुओं के लिए एक अशक्त और अंतर्निहित प्रकारों के लिए डिफ़ॉल्ट मान लौटा सकता है।
केसी एंडरसन

13

मैं इस मुद्दे पर एक सामान्य स्थैतिक विधि के लिए एक सरल मामला चाहता था जो कुछ भी "अशक्त" (या तो संदर्भ प्रकार या Nullables) ले सकता था, जो मुझे इस प्रश्न के लिए कोई संतोषजनक समाधान नहीं मिला। इसलिए मैं अपने स्वयं के समाधान के साथ आया, जो ओपी के घोषित प्रश्न की तुलना में दो ओवरलोड तरीकों से हल करना अपेक्षाकृत आसान था, एक जिसमें Tबाधा होती है where T : classऔर एक और जो एक T?और होती है where T : struct

मुझे तब एहसास हुआ, कि इस समस्या को एक समस्या के समाधान के लिए भी लागू किया जा सकता है, जो कि कंस्ट्रक्टर को निजी (या संरक्षित) बनाकर और स्थिर फैक्टरी विधि का उपयोग करके संकलन समय पर जांच योग्य है:

    //this class is to avoid having to supply generic type arguments 
    //to the static factory call (see CA1000)
    public static class Foo
    {
        public static Foo<TFoo> Create<TFoo>(TFoo value)
            where TFoo : class
        {
            return Foo<TFoo>.Create(value);
        }

        public static Foo<TFoo?> Create<TFoo>(TFoo? value)
            where TFoo : struct
        {
            return Foo<TFoo?>.Create(value);
        }
    }

    public class Foo<T>
    {
        private T item;

        private Foo(T value)
        {
            item = value;
        }

        public bool IsNull()
        {
            return item == null;
        }

        internal static Foo<TFoo> Create<TFoo>(TFoo value)
            where TFoo : class
        {
            return new Foo<TFoo>(value);
        }

        internal static Foo<TFoo?> Create<TFoo>(TFoo? value)
            where TFoo : struct
        {
            return new Foo<TFoo?>(value);
        }
    }

अब हम इसे इस तरह उपयोग कर सकते हैं:

        var foo1 = new Foo<int>(1); //does not compile
        var foo2 = Foo.Create(2); //does not compile
        var foo3 = Foo.Create(""); //compiles
        var foo4 = Foo.Create(new object()); //compiles
        var foo5 = Foo.Create((int?)5); //compiles

यदि आप एक पैरामीटर रहित कंस्ट्रक्टर चाहते हैं, तो आपको ओवरलोडिंग का विकल्प नहीं मिलेगा, लेकिन आप अभी भी ऐसा कुछ कर सकते हैं:

    public static class Foo
    {
        public static Foo<TFoo> Create<TFoo>()
            where TFoo : class
        {
            return Foo<TFoo>.Create<TFoo>();
        }

        public static Foo<TFoo?> CreateNullable<TFoo>()
            where TFoo : struct
        {
            return Foo<TFoo?>.CreateNullable<TFoo>();
        }
    }

    public class Foo<T>
    {
        private T item;

        private Foo()
        {
        }

        public bool IsNull()
        {
            return item == null;
        }

        internal static Foo<TFoo> Create<TFoo>()
            where TFoo : class
        {
            return new Foo<TFoo>();
        }

        internal static Foo<TFoo?> CreateNullable<TFoo>()
            where TFoo : struct
        {
            return new Foo<TFoo?>();
        }
    }

और इसे इस तरह से उपयोग करें:

        var foo1 = new Foo<int>(); //does not compile
        var foo2 = Foo.Create<int>(); //does not compile
        var foo3 = Foo.Create<string>(); //compiles
        var foo4 = Foo.Create<object>(); //compiles
        var foo5 = Foo.CreateNullable<int>(); //compiles

इस समाधान के कुछ नुकसान हैं, एक यह है कि आप वस्तुओं के निर्माण के लिए 'नए' का उपयोग करना पसंद कर सकते हैं। एक और है कि आप का उपयोग करने के लिए सक्षम नहीं होंगे है Foo<T>की तरह कुछ का एक प्रकार बाधा के लिए एक सामान्य प्रकार तर्क के रूप में: where TFoo: new()। अंत में आपको यहां अतिरिक्त कोड की आवश्यकता है जो कि विशेष रूप से बढ़ेगा यदि आपको कई अतिभारित निर्माणकर्ताओं की आवश्यकता है।


8

जैसा कि उल्लेख किया गया है, आपके पास इसके लिए संकलन-समय की जांच नहीं हो सकती है। .NET में सामान्य बाधाओं का गंभीर अभाव है, और अधिकांश परिदृश्यों का समर्थन नहीं करते हैं।

हालाँकि मैं इसे रन-टाइम जाँच के लिए एक बेहतर समाधान मानता हूँ। यह JIT संकलन समय पर अनुकूलित किया जा सकता है, क्योंकि वे दोनों स्थिरांक हैं।

public class SomeClass<T>
{
    public SomeClass()
    {
        // JIT-compile time check, so it doesn't even have to evaluate.
        if (default(T) != null)
            throw new InvalidOperationException("SomeClass<T> requires T to be a nullable type.");

        T variable;
        // This still won't compile
        // variable = null;
        // but because you know it's a nullable type, this works just fine
        variable = default(T);
    }
}

3

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

हालाँकि, आप अपनी आवश्यकताओं के लिए एक अनकांशस टाइप पैरामीटर पर वापस आ सकते हैं, क्योंकि आप हमेशा == के लिए जाँच कर सकते हैं। यदि प्रकार एक मान है, तो चेक हमेशा झूठे का मूल्यांकन करेगा। तब आप संभवतः आर # चेतावनी "नल के साथ मूल्य प्रकार की संभावित तुलना" प्राप्त करेंगे, जो महत्वपूर्ण नहीं है, जब तक कि शब्दार्थ आपके लिए सही नहीं है।

एक विकल्प का उपयोग किया जा सकता है

object.Equals(value, default(T))

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


मुझे लगता है कि समस्या यह है कि उस मूल्य को कैसे निर्धारित किया जाए, इसकी जाँच करें। अशक्त से भिन्न यह इंगित करता है कि मूल्य को प्रारंभिक रूप दिया गया है।
रिसजार्ड डेगन

यह दृष्टिकोण को अमान्य नहीं करता है, क्योंकि मूल्य प्रकार हमेशा सेट होते हैं (कम से कम उनके संबंधित डिफ़ॉल्ट मान के लिए)।
स्वेन अमान

3

मैं उपयोग करता हूं

public class Foo<T> where T: struct
{
    private T? item;
}

-2
    public class Foo<T>
    {
        private T item;

        public Foo(T item)
        {
            this.item = item;
        }

        public bool IsNull()
        {
            return object.Equals(item, null);
        }
    }

    var fooStruct = new Foo<int?>(3);
        var b = fooStruct.IsNull();

        var fooStruct1 = new Foo<int>(3);
        b = fooStruct1.IsNull();

        var fooStruct2 = new Foo<int?>(null);
        b = fooStruct2.IsNull();

        var fooStruct3 = new Foo<string>("qqq");
        b = fooStruct3.IsNull();

        var fooStruct4 = new Foo<string>(null);
        b = fooStruct4.IsNull();

यह टाइपिंग नए फू <int> (42) और इसनॉल () को गलत तरीके से लौटाएगी, जो शब्दार्थिक रूप से सही है, विशेष रूप से सार्थक नहीं है।
आरजे लोहान

1
42 "जीवन का अंतिम प्रश्न, ब्रह्मांड और सब कुछ का उत्तर है"। सीधे शब्दों में कहें: हर int मान के लिए IsNull झूठी (0 मान के लिए भी) वापस आ जाएगी।
रिसजार्ड डेगन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.