সি # জেনেরিক টাইপ সীমাবদ্ধ সমস্ত কিছুর জন্য প্রযোজ্য


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
@ টিম যা নুলাবলসের অনুমতি দেয় না
রিক

এই লিঙ্কটি আপনাকে সহায়তা করতে পারে: social.msdn.microsoft.com/ Forums
en-

2
এটি সরাসরি করা সম্ভব নয়। সম্ভবত আপনি আপনার পরিস্থিতি সম্পর্কে আমাদের আরও বলতে পারেন? অথবা সম্ভবত আপনি IFoo<T>কাজের ধরণ হিসাবে ব্যবহার করতে পারেন এবং একটি কারখানা পদ্ধতির মাধ্যমে উদাহরণ তৈরি করতে পারেন ? কাজ করতে পারে।
জন

আমি নিশ্চিত না যে আপনি কেন এইভাবে কিছু সীমাবদ্ধ করতে চান বা প্রয়োজন হবে। যদি আপনার একমাত্র অভিপ্রায়টি "যদি x == নাল" কে যদি x.IsNull () "এ রূপান্তরিত করে তবে প্রাক্তন বাক্য গঠনতে ব্যবহৃত 99.99% বিকাশকারীদের কাছে এটি অর্থহীন এবং উদ্বেগজনক বলে মনে হচ্ছে The সংকলক আপনাকে করতে দেয় না" যদি (অন্তঃ) x == নল "যাইহোক, তাই আপনি ইতিমধ্যে আচ্ছাদিত।
আরজে লোহান

1
এটি এসও তে বেশ আলোচিত। stackoverflow.com/questions/209160/... এবং stackoverflow.com/questions/13794554/...
বচন Gershkovich

উত্তর:


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
আপনি যদি এটি করতে যাচ্ছেন তবে স্থির নির্মাতার চেকটি নিশ্চিত করেছেন তা নিশ্চিত করুন , অন্যথায় আপনি আপনার জেনেরিক ক্লাসের প্রতিটি ঘটনা (অপ্রয়োজনীয়ভাবে)
কমিয়ে দিবেন

2
@EamonNerbonne আপনি স্ট্যাটিক কনস্ট্রাকটর থেকে ব্যতিক্রম বাড়াতে করা উচিত নয়: msdn.microsoft.com/en-us/library/bb386039.aspx
ম্যাথু ওয়াটসন

5
গাইডলাইনগুলি রহস্যজনক নয়। আপনি যদি এই চেকটি চান, আপনার রানটাইম চেক বনাম স্ট্যাটিক কনস্ট্রাক্টরের ব্যতিক্রমহীনতা ব্যয় করতে হবে। যেহেতু আপনি এখানে সত্যই দুর্বল-ম্যানস স্ট্যাটিক বিশ্লেষক বাস্তবায়ন করছেন, বিকাশের সময় ব্যতীত কখনও কখনও এই ব্যতিক্রম ছোঁড়া উচিত নয়। পরিশেষে, এমনকি যদি আপনি স্থিতিশীল নির্মাণ ব্যতিক্রমগুলি সকল মূল্যে (বুদ্ধিহীনভাবে) এড়াতে চান, তবে আপনার এখনও স্ট্যান্টিক্যালি যতটা সম্ভব কাজ করা উচিত এবং ইনস্ট্যান্স কনস্ট্রাক্টরে যতটা সম্ভব সম্ভব - যেমন একটি পতাকা "ইসবর্কড" বা যা কিছু নির্ধারণ করে।
ইমন নেরবোন

ঘটনাচক্রে, আমি মনে করি না যে আপনার এটি করার চেষ্টা করা উচিত। বেশিরভাগ পরিস্থিতিতে আমি একটি ফাঁসী, ব্যর্থতা-প্রবণতা বিমূর্ততা নিয়ে চেষ্টা এবং কাজ করার চেয়ে বরং এটি কেবলমাত্র একটি # সীমাবদ্ধতা হিসাবে গ্রহণ করতে পছন্দ করব। যেমন একটি পৃথক সমাধান হতে পারে কেবল ক্লাসের প্রয়োজন হয়, বা কেবল স্ট্রাক্টের প্রয়োজন হয় (এবং স্পষ্টতই এটিকে naclable করা হয়) - বা উভয়ই করুন এবং দুটি সংস্করণ রয়েছে। এটি এই সমাধানের সমালোচনা নয়; এটি ঠিক যে এই সমস্যাটি ভালভাবে সমাধান করা যায় না - তা না হলে আপনি কোনও কাস্টম রোসলিন বিশ্লেষক লিখতে রাজি হন না।
ইমন নেরবোন

1
আপনি উভয় পৃথিবীর সেরাটি পেতে পারেন - static bool isValidTypeস্থির নির্মাত্রে আপনি যে ক্ষেত্র স্থাপন করেছেন তা রাখুন, তবে কেবল ইনস্ট্রাক্টরের সেই পতাকাটি পরীক্ষা করুন এবং এটি যদি কোনও অবৈধ প্রকারের হয় তবে ফেলে দিন যাতে আপনি প্রতিবার নির্মাণের সময় সমস্ত পরীক্ষার কাজ করছেন না throw একটি দৃষ্টান্ত. আমি এই প্যাটার্নটি প্রায়শই ব্যবহার করি।
মাইক মেরিনোভস্কি

20

আমি কিভাবে সমতূল্য বাস্তবায়ন জানি না বা জেনেরিক্স হবে। তবে আমি স্থিতিশীল প্রকারের জন্য নাল তৈরি করতে এবং কাঠামোগুলির জন্য 0 মান তৈরি করতে ডিফল্ট কী শব্দটি ব্যবহার করার প্রস্তাব দিতে পারি :

public class Foo<T>
{
    private T item;

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

আপনি আপনার নালার সংস্করণটি প্রয়োগ করতে পারেন:

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
    }
}

কাঠামোর মূল নাল <<> একটি কাঠামো, কোনও শ্রেণি নয়। আমি মনে করি না যে কোনও রেফারেন্স টাইপের মোড়ক তৈরি করা ভাল ধারণা যা কোনও মানের ধরণের নকল করবে।
নিলাল কানফটন

1
ডিফল্ট ব্যবহার করে প্রথম পরামর্শটি নিখুঁত! এখন জেনেরিক টাইপযুক্ত আমার টেম্পলেটটি অবজেক্টগুলির জন্য একটি শূন্যস্থান এবং অন্তর্নির্মিত প্রকারের জন্য ডিফল্ট মান ফিরিয়ে দিতে পারে।
কেসি অ্যান্ডারসন

13

আমি জেনেরিক স্ট্যাটিক পদ্ধতি চাই যা "নুলযোগ্য" (কোনও রেফারেন্স টাইপ বা ন্যুয়েবলস) নিতে পারে এমন একটি সহজ মামলার জন্য এই ইস্যুটিতে ছড়িয়ে পড়েছি, যা সন্তোষজনক সমাধান ছাড়াই আমাকে এই প্রশ্নে নিয়ে এসেছিল। সুতরাং আমি আমার নিজের সমাধানটি নিয়ে এসেছি যা সমাধানের তুলনায় তুলনামূলক সহজতর ছিল ওপি'র বিবৃত প্রশ্নের তুলনায় কেবল দুটি ওভারলোডেড পদ্ধতি, যা একটি নেয় 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 এ জেনেরিক সীমাবদ্ধতার মারাত্মক অভাব রয়েছে এবং বেশিরভাগ পরিস্থিতিতে সমর্থন করে না।

তবে আমি এটিকে রান-টাইম চেকিংয়ের জন্য আরও ভাল সমাধান হিসাবে বিবেচনা করি। এটি জেআইটি সংকলনের সময়ে অনুকূলিত হতে পারে, যেহেতু তারা উভয়ই ধ্রুবক।

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))

নাল চেকের পরিবর্তে, ডিফল্ট (টি) যেহেতু টি: শ্রেণি সর্বদা নাল থাকে। তবে এটির অর্থ হ'ল আপনি আবহাওয়ার পার্থক্য করতে পারবেন না একটি অ-অযোগ্য মান কখনই স্পষ্টভাবে সেট করা হয়নি বা কেবল এটির ডিফল্ট মানতে সেট করা হয়নি।


আমি মনে করি যে সমস্যাটি কীভাবে সেই মানটি কখনই সেট করা হয় নি তা যাচাই করা যায়। নাল থেকে আলাদা মনে হয় যে মানটি আরম্ভ করা হয়েছে।
Ryszard Dżegan

এটি পদ্ধতির অকার্যকর হয় না, যেহেতু মানের ধরণগুলি সর্বদা সেট থাকে (কমপক্ষে স্ব স্ব স্ব স্ব স্বনির্ধারিত মানের জন্য অন্তর্ভুক্ত)।
সোভেন আমান

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 হ'ল "জীবনের চূড়ান্ত প্রশ্নের উত্তর, মহাবিশ্ব এবং সবকিছু" " সোজা কথায়: প্রতিটি আন্তঃ মানের জন্য ইসনুল মিথ্যা (এমনকি 0 মানের ক্ষেত্রেও) ফিরে আসবে।
Ryszard Dżegan
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.