سؤال

المشكلة:

class StatesChain : IState, IHasStateList {
    private TasksChain tasks = new TasksChain();

     ...

    public IList<IState> States {
        get { return _taskChain.Tasks; }
    }

    IList<ITask> IHasTasksCollection.Tasks {
        get { return _taskChain.Tasks; } <-- ERROR! You can't do this in C#!
                                             I want to return an IList<ITask> from
                                             an IList<IStates>.
    }
}

على افتراض IList سيتم إرجاعه للقراءة فقط ، وأنا أعلم أن ما أحاول تحقيقه آمن (أم أنه ليس كذلك؟). هل هناك أي طريقة يمكنني إنجاز ما أحاول؟ لا أريد أن أحاول تنفيذ نفسي TasksChain الخوارزمية (مرة أخرى!) ، لأنها ستكون عرضة للخطأ وستؤدي إلى ازدواج رمز. ربما يمكنني فقط تحديد سلسلة مجردة ثم تنفيذ كليهما TasksChain و StatesChain من هناك؟ أو ربما تنفيذ أ Chain<T> صف دراسي؟

كيف ستتعامل مع هذا الموقف؟

التفاصيل:لقد حددت ITask واجهه المستخدم:

public interface ITask {
    bool Run();
    ITask FailureTask { get; }
}

و IState الواجهة التي ترث من ITask:

public interface IState : ITask {
    IState FailureState { get; }
}

لقد حددت أيضا IHasTasksList واجهه المستخدم:

interface IHasTasksList {
    List<Tasks> Tasks { get; }
}

و IHasStatesList:

interface IHasTasksList {
    List<Tasks> States { get; }
}

الآن ، لقد حددت أ TasksChain, ، هذا فئة تحتوي على بعض منطق الرمز الذي سيتناول سلسلة من المهام (احذروا ذلك TasksChain هو نفسه نوع من ITask!):

class TasksChain : ITask, IHasTasksList {
    IList<ITask> tasks = new List<ITask>();

    ...

    public List<ITask> Tasks { get { return _tasks; } }

    ...
}

أنا أقوم بتنفيذ أ State الطريقة التالية:

public class State : IState {
    private readonly TaskChain _taskChain = new TaskChain();

    public State(Precondition precondition, Execution execution) {
        _taskChain.Tasks.Add(precondition);
        _taskChain.Tasks.Add(execution);
    }

    public bool Run() {
        return _taskChain.Run();
    }

    public IState FailureState {
        get { return (IState)_taskChain.Tasks[0].FailureTask; }
    }

    ITask ITask.FailureTask {
        get { return FailureState; }
    }
}

والتي ، كما ترون ، تستخدم تطبيقات الواجهة الصريحة لـ "الاختباء" FailureTask وبدلا من ذلك تظهر FailureState منشأه.

المشكلة تأتي من حقيقة أنني أريد أيضًا تحديد أ StatesChain, أن يرث كلاهما من IState و IHasStateList (وهذا أيضا الانبعاثات ITask و IHasTaskList, ، تم تنفيذه كواجهات صريحة) وأريد أن يختبئ أيضًا IHasTaskListTasks وعرض فقط IHasStateListStates. (يجب أن يكون قسم "The Problem" الوارد في "المشكلة" حقًا بعد ذلك ، لكنني اعتقدت أن وضعه أولاً سيكون أكثر ودية للقارئ).

(pff..ong نص) شكرًا!

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

المحلول

على السطر حيث تحصل على خطأ ، تحاول العودة IList<IStates> كما لو كان مثيلًا للنوع IList<ITask>. هذا لا يعمل تلقائيًا ، لأن النوعين مختلفان (بغض النظر عن أن المعلمات العامة مرتبطة).

في C# 3.0 أو أكبر ، لا توجد طريقة لتحقيق ذلك تلقائيًا. يضيف C# 4.0 دعمًا للتغاير والتناقض ، والذي يخدم هذا الغرض بالضبط. ولكن كما لاحظت ، هذا لا يعمل إلا عندما تكون المجموعة التي تم إرجاعها للقراءة فقط. ال IList<T> النوع لا يضمن ذلك ، لذلك لم يتم شرحه تشترك في .NET 4.0.

لجعل هذا العمل باستخدام C# 4.0 ، ستحتاج إلى استخدام نوع القراءة فقط, ، الذي يحتوي على شرح متغير في الإطار - الخيار الأفضل في قضيتك IEnumerable<T> (على الرغم من أنه يمكنك تحديد خاص بك باستخدام out T المعدل).

لإضافة المزيد من التفاصيل ، في C# 4.0 ، يمكنك إعلان واجهة إما متغير أو متغير. تعني الحالة الأولى أن المترجم سيسمح لك بإجراء التحويل الذي تحتاجه في مثالك (الحالة الأخرى مفيدة لفئات الكتابة فقط). يتم ذلك عن طريق إضافة تعليقات توضيحية صريحة إلى إعلان الواجهة (هذه متوفرة بالفعل لأنواع .NET 4.0). على سبيل المثال ، إعلان IEnumerable<T> لديه out التعليق التوضيحي يعني أنه يدعم التباين:

public interface IEnumerable<out T> : IEnumerable { /* ... */ }

الآن ، سيسمح لك المترجم بالكتابة:

IEnumerable<IState> states = ...
IEnumerable<ITask> tasks = states;

نصائح أخرى

باختصار ، لا ، إنه ليس آمنًا ، لأن "القراءة فقط" IList<> غير موجود (العقد). إنه فقط التنفيذ الذي سيرفض الإدخالات ، ولكن هذا متأخر جدًا ، لأن المكالمة نفسها تتطلب أن تكون معلمة نوع الواجهة متضايقة في نفس الوقت.

يمكنك ، ومع ذلك ، إعادة IEnumerable<> بدلاً من ذلك ، وهو متغير في C# 4. نظرًا لأن هذا يكفي لاستخدام LINQ ، فلا ينبغي أن يكون هذا العيب ويعبر عن الطبيعة فقط بشكل أفضل.

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