সদস্য সংগ্রহগুলি প্রকাশের জন্য কেবলমাত্র সংগ্রহশালা বা আইনিংরেবল?


124

কলিং কোডটি যদি সংগ্রহের উপরে কেবল পুনরাবৃত্তি করে তবে কোনও আইনিউবারেবলের চেয়ে কেবলমাত্র পঠনকলি সংগ্রহ হিসাবে অভ্যন্তরীণ সংগ্রহকে প্রকাশ করার কোনও কারণ আছে?

class Bar
{
    private ICollection<Foo> foos;

    // Which one is to be preferred?
    public IEnumerable<Foo> Foos { ... }
    public ReadOnlyCollection<Foo> Foos { ... }
}


// Calling code:

foreach (var f in bar.Foos)
    DoSomething(f);

যেহেতু আমি দেখতে পাচ্ছি আইনিম্যারেবল হ'ল রিডইনলি ক্লেকশন-এর ইন্টারফেসের একটি উপসেট এবং এটি ব্যবহারকারীর সংগ্রহে পরিবর্তন করতে দেয় না। সুতরাং যদি আইনামবারেবল ইন্টারফেসটি যথেষ্ট হয় তবে এটিই ব্যবহারযোগ্য। এটি কি এটি সম্পর্কে তর্ক করার উপযুক্ত উপায় বা আমি কিছু মিস করছি?

ধন্যবাদ / এরিক


6
আপনি .NET 4.5 ব্যবহার করছেন, আপনি নতুন পঠনযোগ্য কেবল সংগ্রহ ইন্টারফেস চেষ্টা করতে চাইতে পারেন । আপনি ReadOnlyCollectionযদি অদ্ভুত হন তবে আপনি ফেরত সংগ্রহটি মুড়ে রাখতে চাইবেন তবে এখন আপনি একটি নির্দিষ্ট প্রয়োগের সাথে আবদ্ধ নন।
স্ট্রিপলিং ওয়ারিয়র

উত্তর:


96

আরও আধুনিক সমাধান

অভ্যন্তরীণ সংগ্রহটি পরিবর্তনযোগ্য হতে না হলে আপনি System.Collections.Immutableপ্যাকেজটি ব্যবহার করতে পারবেন , আপনার ক্ষেত্রের প্রকারকে অপরিবর্তনীয় সংগ্রহ হিসাবে পরিবর্তন করতে পারবেন এবং তারপরে সরাসরি এটি প্রকাশ করতে পারেন - Fooঅবশ্যই ধরে নিবেন যে নিজেকে অবশ্যই পরিবর্তনযোগ্য।

প্রশ্নটি আরও সরাসরি সমাধানের জন্য আপডেট করা উত্তর

কলিং কোডটি যদি সংগ্রহের উপরে কেবল পুনরাবৃত্তি করে তবে কোনও আইনিউবারেবলের চেয়ে কেবলমাত্র পঠনকলি সংগ্রহ হিসাবে অভ্যন্তরীণ সংগ্রহকে প্রকাশ করার কোনও কারণ আছে?

এটি নির্ভর করে আপনি কলিং কোডটিতে কতটা বিশ্বাস করেন। আপনি যদি এই সদস্যকে কখনও কল করবেন এমন সমস্ত কিছুর উপর যদি আপনি সম্পূর্ণ নিয়ন্ত্রণে থাকেন এবং আপনি গ্যারান্টি দেন যে কোনও কোড কখনও ব্যবহার করবে না:

ICollection<Foo> evil = (ICollection<Foo>) bar.Foos;
evil.Add(...);

তবে নিশ্চিত, আপনি যদি সরাসরি সংগ্রহটি ফিরিয়ে দেন তবে কোনও ক্ষতি করা হবে না। আমি যদিও এর চেয়ে কিছুটা বেশি বেহাল হতে চেষ্টা করি।

তেমনি, আপনি যেমন বলেছেন: আপনার যদি কেবল প্রয়োজন হয় IEnumerable<T> , তবে কেন নিজেকে আরও শক্তিশালী কিছুতে বেঁধে রাখুন?

আসল উত্তর

আপনি .NET 3.5 ব্যবহার করছেন, আপনি একটি অনুলিপি তৈরি এড়াতে পারবেন এবং এড়াতে সরল কল ব্যবহার করে সাধারণ কাস্ট এড়াতে পারেন:

public IEnumerable<Foo> Foos {
    get { return foos.Skip(0); }
}

(তুচ্ছভাবে মোড়ানোর জন্য প্রচুর অন্যান্য বিকল্প রয়েছে - Skipসিলেক্ট / ওভার ওভার সম্পর্কে দুর্দান্ত জিনিসটি প্রতিটি পুনরাবৃত্তির জন্য নিরর্থকভাবে কার্যকর করার কোনও প্রতিনিধি নেই))

আপনি যদি .NET 3.5 ব্যবহার না করে থাকেন তবে একই জিনিসটি করতে আপনি খুব সাধারণ একটি র‍্যাপার লিখতে পারেন:

public static IEnumerable<T> Wrapper<T>(IEnumerable<T> source)
{
    foreach (T element in source)
    {
        yield return element;
    }
}

15
নোট এই জন্য একটি কার্যকারিতা শাস্তি যে: আমি যতদূর জানি Enumerable.Count পদ্ধতি IEnumerables মধ্যে casted সংগ্রহগুলি জন্য অপ্টিমাইজ করা হয়, কিন্তু একটি রেঞ্জ এড়িয়ে দ্বারা উত্পাদিত জন্য নয় (0)
shojtsy

6
-1 এটি কি কেবল আমিই বা এটি একটি অন্য প্রশ্নের উত্তর? এটি এই "সমাধান" জানার জন্য দরকারী, তবে বিকল্পটি একটি রিডইনলি সংগ্রহ এবং আইইনুমেবল ফিরে পাওয়ার মধ্যে একটি তুলনা চেয়েছে। এই উত্তরটি ইতিমধ্যে ধরে নিয়েছে যে আপনি এই সিদ্ধান্তকে সমর্থন করার কোনও যুক্তি ছাড়াই মূল সংখ্যাটি ফিরিয়ে দিতে চান।
জায়েদ মাসুদ

1
উত্তর শো একটি ভোটদান ReadOnlyCollectionএকটি থেকে ICollectionএবং তারপর চলমান Addসফলভাবে। আমি যতদূর জানি, এটি সম্ভব হওয়া উচিত নয়। evil.Add(...)একটি ত্রুটি নিক্ষেপ করা উচিত নয়। dotnetfiddle.net/GII2jW
শন লুটিন

1
@ শানলুটিন: হ্যাঁ, ঠিক - এটি ছুড়ে ফেলেছে। তবে আমার উত্তরে আমি কাস্ট করি না ReadOnlyCollection- আমি IEnumerable<T>এমন একটি কাস্ট করি যা আসলেই কেবল পঠিত হয় না ... এটি প্রকাশের পরিবর্তেReadOnlyCollection
জোন স্কিট

1
আহা। আপনার প্যারানাইয়া খারাপ কাস্টিং থেকে রক্ষা করার জন্য আপনাকে একটি ReadOnlyCollectionপরিবর্তে একটি ব্যবহার করতে পরিচালিত করবে IEnumerable
শন লুটিন

43

আপনার যদি কেবল সংগ্রহের মাধ্যমে পুনরাবৃত্তি করতে হয়:

foreach (Foo f in bar.Foos)

তারপরে IENumerable ফিরিয়ে দেওয়া যথেষ্ট।

আপনার যদি আইটেমগুলিতে এলোমেলো অ্যাক্সেসের প্রয়োজন হয়:

Foo f = bar.Foos[17];

তারপরে এটি ReadOnly Colલેક્શનমোড়ুন


@ ভোজিসলাভ স্টোজকোভিক, +1। এই পরামর্শ কি আজও প্রযোজ্য?
w0051977

1
@ w0051977 এটি আপনার অভিপ্রায়ের উপর নির্ভর করে। System.Collections.Immutableজোন স্কিটের পরামর্শ অনুসারে ব্যবহার করা পুরোপুরি বৈধ, যখন আপনি এমন কোনও বিষয় প্রকাশ করছেন যা আপনার কোনও উল্লেখ পাওয়ার পরে কখনও পরিবর্তন হবে না change অন্যদিকে, আপনি যদি কোনও পরিবর্তনীয় সংগ্রহের কেবল পঠনযোগ্য দর্শন প্রকাশ করছেন তবে এই পরামর্শটি এখনও কার্যকর is যদিও আমি সম্ভবত এটি প্রকাশ করব IReadOnlyCollection(যা আমি এটি লেখার সময় উপস্থিত ছিল না)।
ভোজিসলাভ স্টোজকোভিক 14

@VojislavStojkovic আপনি ব্যবহার করতে পারবেন না bar.Foos[17]সঙ্গে IReadOnlyCollection। পার্থক্য কেবলমাত্র অতিরিক্ত Countসম্পত্তি। public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable
কনরাড

@ কনরাড রাইট, আপনার যদি প্রয়োজন হয় তবে আপনাকে bar.Foos[17]এটি প্রকাশ করতে হবে ReadOnlyCollection<Foo>। আপনি যদি আকারটি জানতে চান এবং এর মাধ্যমে পুনরাবৃত্তি করতে চান তবে এটি প্রকাশ করুন IReadOnlyCollection<Foo>। আপনার যদি আকার জানার প্রয়োজন না হয় তবে ব্যবহার করুন IEnumerable<Foo>। অন্যান্য সমাধান এবং অন্যান্য বিশদ রয়েছে, তবে এটি এই নির্দিষ্ট উত্তরটির আলোচনার বাইরে।
ভোজিসলাভ স্টোজকোভিক

31

আপনি যদি এটি করেন তবে আপনার কলাররা আইকোল্লেক্সনে আইননামারেবলটি পিছনে ফেলে এবং তারপরে সংশোধন করার কিছু নেই। রিডইনলি সংগ্রহটি এই সম্ভাবনাটি সরিয়ে দেয়, যদিও প্রতিফলনের মাধ্যমে অন্তর্নিহিত লিখনযোগ্য সংগ্রহটি অ্যাক্সেস করা এখনও সম্ভব। যদি সংগ্রহটি ছোট হয় তবে এই সমস্যাটি ঘুরে দেখার নিরাপদ এবং সহজ উপায় হ'ল পরিবর্তে একটি অনুলিপি ফেরানো।


14
এটাই "ভৌতিক ডিজাইন" এর সাথে আমি আন্তরিকভাবে একমত নই। আমি মনে করি একটি নীতি প্রয়োগের জন্য অপর্যাপ্ত শব্দার্থক নির্বাচন করা খারাপ অভ্যাস।
ভোজিসলাভ স্টোজকোভিক

24
আপনি দ্বিমত পোষণ করে এটি ভুল করে না। আমি যদি বাহ্যিক বিতরণের জন্য একটি লাইব্রেরি ডিজাইন করি তবে API এর অপব্যবহার করার চেষ্টা করা ব্যবহারকারীদের দ্বারা উত্থাপিত বাগগুলির সাথে ডিল করার চেয়ে আমার অনেক বেশি একটি অমূলক ইন্টারফেস থাকতে হবে। সিএস
স্টু ম্যাক্কেলার

5
হ্যাঁ, বাহ্যিক বিতরণের জন্য একটি লাইব্রেরি ডিজাইনের নির্দিষ্ট ক্ষেত্রে, আপনি যেটাকে প্রকাশ করছেন তার জন্য একটি ভৌতিক ইন্টারফেস থাকা বোধগম্য। তবুও, যদি ফুসের সম্পত্তিটির শব্দার্থবিজ্ঞান অনুক্রমিক অ্যাক্সেস হয়, তবে কেবলমাত্র পঠন-কল্পনা সংগ্রহ করুন এবং তারপরে আইমুনিউরেবল ফিরিয়ে দিন। ভুল শব্দার্থবিজ্ঞান ব্যবহার করার দরকার নেই;)
ভোজিসলাভ স্টোজকভিক

9
আপনারও করার দরকার নেই। আপনি অনুলিপি ছাড়াই কেবল পঠনযোগ্য পুনরুক্তি ফেরত দিতে পারেন ...
জন স্কিটি

1
@ shojtsy আপনার কোড লাইনটি কাজ করছে বলে মনে হচ্ছে না। যেমন একটি নাল উত্পন্ন । একটি কাস্ট ব্যতিক্রম ছোঁড়ে।
নিক আলেক্সেভ

3

আমি যতটা সম্ভব রিডইনলি সংগ্রহ ব্যবহার করা এড়াচ্ছি, এটি কেবলমাত্র একটি সাধারণ তালিকা ব্যবহারের চেয়ে যথেষ্ট ধীর। এই উদাহরণটি দেখুন:

List<int> intList = new List<int>();
        //Use a ReadOnlyCollection around the List
        System.Collections.ObjectModel.ReadOnlyCollection<int> mValue = new System.Collections.ObjectModel.ReadOnlyCollection<int>(intList);

        for (int i = 0; i < 100000000; i++)
        {
            intList.Add(i);
        }
        long result = 0;

        //Use normal foreach on the ReadOnlyCollection
        TimeSpan lStart = new TimeSpan(System.DateTime.Now.Ticks);
        foreach (int i in mValue)
            result += i;
        TimeSpan lEnd = new TimeSpan(System.DateTime.Now.Ticks);
        MessageBox.Show("Speed(ms): " + (lEnd.TotalMilliseconds - lStart.TotalMilliseconds).ToString());
        MessageBox.Show("Result: " + result.ToString());

        //use <list>.ForEach
        lStart = new TimeSpan(System.DateTime.Now.Ticks);
        result = 0;
        intList.ForEach(delegate(int i) { result += i; });
        lEnd = new TimeSpan(System.DateTime.Now.Ticks);
        MessageBox.Show("Speed(ms): " + (lEnd.TotalMilliseconds - lStart.TotalMilliseconds).ToString());
        MessageBox.Show("Result: " + result.ToString());

15
অকাল অপ্টিমাইজেশন। আমার পরিমাপ অনুসারে, কেবলমাত্র ReadOnly সংগ্রহের মাধ্যমে পুনরাবৃত্তি করা নিয়মিত তালিকার চেয়ে প্রায় 36% বেশি সময় নেয়, ধরে নিই যে লুপের জন্য আপনি ভিতরে কিছুই করেন না । সম্ভাবনাগুলি হ'ল, আপনি কখনই এই পার্থক্যটি লক্ষ্য করতে পারবেন না, তবে এটি যদি আপনার কোডের একটি হট স্পট এবং প্রতিটি শেষ বিট পারফরম্যান্সের প্রয়োজন হয় তবে আপনি এর বাইরে বেরিয়ে যেতে পারেন, পরিবর্তে কোনও অ্যারে ব্যবহার করবেন না কেন? এটি আরও দ্রুত হতে হবে।
স্ট্রিপলিং ওয়ারিয়র

আপনার মানদণ্ডে ত্রুটি রয়েছে, আপনি শাখার পূর্বাভাসটিকে সম্পূর্ণ উপেক্ষা করছেন।
ট্রোজান

0

কখনও কখনও আপনি একটি ইন্টারফেস ব্যবহার করতে চাইতে পারেন, সম্ভবত আপনি ইউনিট পরীক্ষার সময় সংগ্রহটি উপহাস করতে চান। একটি অ্যাডাপ্টার ব্যবহার করে আপনার নিজের ইন্টারফেসটি রিডনোলিলেকশন যুক্ত করার জন্য দয়া করে আমার ব্লগ এন্ট্রি দেখুন ।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.