هل المصفوفات غير القابلة للتغيير ممكنة في .NET؟

StackOverflow https://stackoverflow.com/questions/210428

  •  03-07-2019
  •  | 
  •  

سؤال

هل من الممكن وضع علامة أ System.Array كما لا يمكن التوقف. عندما يتم وضعها وراء مجموعة عامة/خاصة ، لا يمكن إضافتها إليها ، لأنها تتطلب إعادة التخصيص وإعادة التقييم ، ولكن لا يزال بإمكان المستهلك تعيين أي ترجمة يرغبون في ذلك:

public class Immy
{
    public string[] { get; private set; }
}

اعتقدت readonly قد تقوم الكلمة الرئيسية بالخدعة ، ولكن لا حظ من هذا القبيل.

هل كانت مفيدة؟

المحلول

ReadOnlyCollection<T> ربما ما تبحث عنه. ليس لديها Add() طريقة.

نصائح أخرى

ال إرشادات تصميم الإطار اقترح إرجاع نسخة من المصفوفة. بهذه الطريقة ، لا يمكن للمستهلكين تغيير العناصر من الصفيف.

// bad code
// could still do Path.InvalidPathChars[0] = 'A';
public sealed class Path {
   public static readonly char[] InvalidPathChars = 
      { '\"', '<', '>', '|' };
}

هذه أفضل:

public static ReadOnlyCollection<char> GetInvalidPathChars(){
   return Array.AsReadOnly(InvalidPathChars);
}

public static char[] GetInvalidPathChars(){
   return (char[])InvalidPathChars.Clone();
}

الأمثلة مباشرة من الكتاب.

لطفا أنظر مجموعات غير قابلة للتغيير متوفرة الآن في مكتبة الفئة الأساسية (حاليًا في المعاينة).

يمكنك استخدام Array.Asreadonly للعودة.

أعتقد أن أفضل الممارسات هو استخدام Ilistu003CT> بدلاً من المصفوفات في واجهات برمجة التطبيقات العامة لهذا السبب الدقيق. يقرأ فقط سيمنع متغير الأعضاء من وضعه خارج المُنشئ ، ولكن كما اكتشفت ، لن يمنع الأشخاص من تعيين عناصر في الصفيف.

نرى المصفوفات تعتبر ضارة إلى حد ما للمزيد من المعلومات.

يحرر: لا يمكن قراءة المصفوفات فقط ، ولكن يمكن تحويلها إلى تطبيقات ilist للقراءة فقط عبر Array.Asreadonly () كما يشير Shahkalpesh.

يميل .NET إلى الابتعاد عن المصفوفات لجميع حالات الاستخدام الأبسط والأكثر تقليدية. لكل شيء آخر ، هناك العديد من تطبيقات التعداد/التجميع.

عندما تريد وضع علامة على مجموعة من البيانات على أنها غير قابلة للتغيير ، فأنت تتجاوز القدرة التي توفرها صفيف تقليدي. يوفر .NET القدرة المكافئة ، ولكن ليس تقنيًا في شكل صفيف. للحصول على مجموعة ثابتة من صفيف ، استخدم Array.AsReadOnly<T>:

var mutable = new[]
{
    'a', 'A',
    'b', 'B',
    'c', 'C',
};

var immutable = Array.AsReadOnly(mutable);

immutable سوف يكون ReadOnlyCollection<char> نموذج. كحالة استخدام أكثر عمومية ، يمكنك إنشاء ملف ReadOnlyCollection<T> من أي عام IList<T> تطبيق.

var immutable = new ReadOnlyCollection<char>(new List<char>(mutable));

لاحظ أنه يجب أن يكون تطبيقًا عامًا ؛ السهول القديمة IList لن تعمل ، وهذا يعني أنه لا يمكنك استخدام هذه الطريقة على صفيف تقليدي ، والذي ينفذ فقط IList. هذا يضيء إمكانية الاستخدام Array.AsReadOnly<T> كوسيلة سريعة للحصول على الوصول إلى التطبيقات العامة التي لا يمكن الوصول إليها عادة عبر صفيف تقليدي.

ReadOnlyCollection<T> سوف تمنحك إمكانية الوصول إلى جميع الميزات التي تتوقعها من صفيف غير قابل للتغيير:

// Note that .NET favors Count over Length; all but traditional arrays use Count:
for (var i = 0; i < immutable.Count; i++)
{
    // this[] { get } is present, as ReadOnlyCollection<T> implements IList<T>:
    var element = immutable[i]; // Works

    // this[] { set } has to be present, as it is required by IList<T>, but it
    // will throw a NotSupportedException:
    immutable[i] = element; // Exception!
}

// ReadOnlyCollection<T> implements IEnumerable<T>, of course:
foreach (var character in immutable)
{
}

// LINQ works fine; idem
var lowercase =
    from c in immutable
    where c >= 'a' && c <= 'z'
    select c;

// You can always evaluate IEnumerable<T> implementations to arrays with LINQ:
var mutableCopy = immutable.ToArray();
// mutableCopy is: new[] { 'a', 'A', 'b', 'B', 'c', 'C' }
var lowercaseArray = lowercase.ToArray();
// lowercaseArray is: new[] { 'a', 'b', 'c' }

الشيء الوحيد الذي يجب إضافته هو تلك المصفوفات لمح التحولية. عندما تقوم بإرجاع صفيف من وظيفة ، فأنت تقترح على مبرمج العميل أنه يمكن/يجب أن يغيروا الأشياء.

علاوة على إجابة Matt ، فإن Ilist هي واجهة مجردة كاملة لمجموعة ، لذلك تسمح بإضافة وإزالة وما إلى ذلك. لست متأكدًا من سبب ظهور Lippert كبديل عن عدم الحاجة إلى عدم الحاجة. ((يحرر: لأن تطبيق IList يمكن أن يلقي استثناءات لتلك الأساليب المتحولة ، إذا كنت تحب هذا النوع من الأشياء).

ربما شيء آخر يجب أن تضع في اعتبارك أن العناصر الموجودة في القائمة قد يكون لها أيضًا حالة قابلة للتغيير. إذا كنت لا تريد حقًا أن يقوم المتصل بتعديل هذه الحالة ، فلديك بعض الخيارات:

تأكد من أن العناصر الموجودة في القائمة غير قابلة للتغيير (كما في مثالك: السلسلة غير قابلة للتغيير).

أعد استنساخًا عميقًا لكل شيء ، لذلك في هذه الحالة ، يمكنك استخدام صفيف على أي حال.

إرجاع واجهة تتيح الوصول إلى عنصر:

interface IImmutable
{
    public string ValuableCustomerData { get; }
}

class Mutable, IImmutable
{
    public string ValuableCustomerData { get; set; }
}

public class Immy
{
    private List<Mutable> _mutableList = new List<Mutable>();

    public IEnumerable<IImmutable> ImmutableItems
    {
        get { return _mutableList.Cast<IMutable>(); }
    }
}

لاحظ أن كل قيمة يمكن الوصول إليها من واجهة iimmutable يجب أن تكون نفسها غير قابلة للتغيير (مثل السلسلة) ، أو أن تكون نسخة أخرى تقوم بها على نحو ممكن.

أفضل ما يمكنك أن تأمل في القيام به هو تمديد مجموعة موجودة لبناء خاص بك. المشكلة الكبيرة هي أنه سيتعين عليها العمل بشكل مختلف عن كل نوع مجموعة موجود لأن كل مكالمة سيتعين عليها إرجاع مجموعة جديدة.

قد ترغب في التحقق إجابتي إلى سؤال مماثل لمزيد من الأفكار حول تعريض المجموعات على كائن.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top